Browse Source

12.25

master
lichong 3 months ago
parent
commit
63ba20e86d
  1. 4
      src/renderer/index.html
  2. 14
      src/renderer/src/App.vue
  3. 8
      src/renderer/src/assets/css/base.css
  4. BIN
      src/renderer/src/assets/excel/19年1月销售汇总.xls
  5. BIN
      src/renderer/src/assets/excel/20年1月销售汇总.xls
  6. BIN
      src/renderer/src/assets/excel/21年1月销售汇总.xls
  7. BIN
      src/renderer/src/assets/excel/22年1月销售汇总.xls
  8. BIN
      src/renderer/src/assets/excel/数据导入.xlsx
  9. 2
      src/renderer/src/assets/js/db.js
  10. 90
      src/renderer/src/assets/json/sale.json
  11. 42
      src/renderer/src/assets/json/shujudui.json
  12. 55
      src/renderer/src/components/formcomponent.vue
  13. 29
      src/renderer/src/components/tablecomponent.vue
  14. 110
      src/renderer/src/views/sale.vue
  15. 267
      src/renderer/src/views/shujucal.vue

4
src/renderer/index.html

@ -3,9 +3,7 @@
<head>
<meta charset="UTF-8" />
<title>数据计算</title>
<!-- <link rel="icon" href="/icon/icon.jpg" /> -->
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<title>销售统计</title>
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self';img-src 'self' data:; style-src 'self' 'unsafe-inline';font-src 'self' data:;" />
</head>

14
src/renderer/src/App.vue

@ -1,14 +1,6 @@
<template>
<div class="appClass" v-if="isVip">
<el-tabs tab-position="left" class="appClass" v-model="tabName">
<el-tab-pane label="数据计算" name="shujucal">
<shujucalComponent></shujucalComponent>
</el-tab-pane>
<el-tab-pane label="数据设置" name="shujudui">
<shujuduiComponent ref="shujudui"> </shujuduiComponent>
</el-tab-pane>
</el-tabs>
<shujucalComponent></shujucalComponent>
</div>
<div v-else class="noVip">
<h3>
@ -29,7 +21,7 @@ export default {
return {
_: _,
dayjs: dayjs,
isVip: true,
isVip: false,
jihuoma: "",
zhucema: "",
tabName: "shujucal",
@ -40,7 +32,7 @@ export default {
methods: {
},
async mounted() {
let fiveDay = dayjs('2024-11-15T00:00:00').valueOf()
let fiveDay = dayjs('2025-01-15T00:00:00').valueOf()
if (!this.isVip) {
if (dayjs().valueOf() > fiveDay) {
this.isVip = false

8
src/renderer/src/assets/css/base.css

@ -2,12 +2,4 @@ html,
body {
margin: 0;
padding: 0;
}
.red {
color: #f56c6c;
}
.blue {
color: #409eff;
}

BIN
src/renderer/src/assets/excel/19年1月销售汇总.xls

Binary file not shown.

BIN
src/renderer/src/assets/excel/20年1月销售汇总.xls

Binary file not shown.

BIN
src/renderer/src/assets/excel/21年1月销售汇总.xls

Binary file not shown.

BIN
src/renderer/src/assets/excel/22年1月销售汇总.xls

Binary file not shown.

BIN
src/renderer/src/assets/excel/数据导入.xlsx

Binary file not shown.

2
src/renderer/src/assets/js/db.js

@ -4,5 +4,5 @@ export const myDatabase = new Dexie('myDatabase');
// 定义数据库版本
myDatabase.version(2).stores({
shujudui: '++id, name, &data, note, create_at, update_at',
sale: '++id, 货号, 品名, 供应商名称, 销售数量, 销售金额, 退货数量, 退货金额, 数量小计, 金额小计, &allInfo, create_at, update_at',
});

90
src/renderer/src/assets/json/sale.json

@ -0,0 +1,90 @@
[
{
"label": "货号",
"prop": "货号",
"type": "text",
"isSort": true,
"tableShow": true,
"formShow": true
},
{
"label": "品名",
"prop": "品名",
"type": "text",
"isSort": true,
"tableShow": true,
"formShow": true
},
{
"label": "供应商名称",
"prop": "供应商名称",
"type": "text",
"isSort": true,
"tableShow": true,
"formShow": true
},
{
"label": "销售数量",
"prop": "销售数量",
"type": "text",
"isSort": true,
"tableShow": true,
"formShow": true
},
{
"label": "销售金额",
"prop": "销售金额",
"type": "text",
"isSort": true,
"tableShow": true,
"formShow": true
},
{
"label": "退货数量",
"prop": "退货数量",
"type": "text",
"isSort": true,
"tableShow": true,
"formShow": true
},
{
"label": "退货金额",
"prop": "退货金额",
"type": "text",
"isSort": true,
"tableShow": true,
"formShow": true
},
{
"label": "数量小计",
"prop": "数量小计",
"type": "text",
"isSort": true,
"tableShow": true,
"formShow": true
},
{
"label": "金额小计",
"prop": "金额小计",
"type": "text",
"isSort": true,
"tableShow": true,
"formShow": true
},
{
"label": "创建时间",
"prop": "create_at",
"type": "date",
"isSort": false,
"tableShow": false,
"formShow": false
},
{
"label": "更新时间",
"prop": "update_at",
"type": "date",
"isSort": false,
"tableShow": false,
"formShow": false
}
]

42
src/renderer/src/assets/json/shujudui.json

@ -1,42 +0,0 @@
[
{
"label": "名称",
"prop": "name",
"type": "text",
"isSort": false,
"tableShow": true,
"formShow": true
},
{
"label": "数据堆",
"prop": "data",
"type": "textarea",
"isSort": false,
"tableShow": true,
"formShow": true
},
{
"label": "备注",
"prop": "note",
"type": "textarea",
"isSort": false,
"tableShow": true,
"formShow": true
},
{
"label": "创建时间",
"prop": "create_at",
"type": "date",
"isSort": false,
"tableShow": false,
"formShow": false
},
{
"label": "更新时间",
"prop": "update_at",
"type": "date",
"isSort": false,
"tableShow": false,
"formShow": false
}
]

55
src/renderer/src/components/formcomponent.vue

@ -2,12 +2,11 @@
<div class="tableClass">
<el-form :model="formData" label-suffix="" :disabled="disabled" @submit.prevent>
<el-row>
<el-col :key="formIndex" v-for="(formItem, formIndex) in formHeader"
:span="['text', 'date', 'textarea'].includes(formItem.type) ? 12 : 12">
<el-col :key="formIndex" v-for="(formItem, formIndex) in formHeader" :span="4">
<el-form-item :label="formItem.label">
<template v-if="['text', 'textarea'].includes(formItem.type)">
<el-input v-model="formData[formItem.prop]" :type="formItem.type" :autosize="{ minRows: 3, maxRows: 15 }"
style="width: 80%;" />
style="width: 95%;" />
</template>
<template v-else-if="formItem.type === 'date'">
<el-date-picker v-model="formData[formItem.prop]" type="date"
@ -55,55 +54,7 @@ export default {
dayjs: dayjs,
}
},
methods: {
async openVedio(formItem) {
try {
this.formData[formItem.prop] = ""
let stream = await navigator.mediaDevices.getUserMedia({
video: {
width: { ideal: 200 },
height: { ideal: 200 }
},
});
this.$refs[`${formItem.prop}video`][0].srcObject = stream;
} catch (e) {
ElMessage({
type: 'error',
message: '摄像头权限被禁止,请打开摄像头权限',
})
return
}
},
takePhoto(formItem) {
let video = this.$refs[`${formItem.prop}video`][0]
let canvas = this.$refs[`${formItem.prop}canvas`][0]
let context = canvas.getContext('2d');
context.drawImage(video, 0, 0, 202, 202);
this.formData[formItem.prop] = canvas.toDataURL('image/png');
},
//
focus(formItem) {
if (_.get(this.$refs[`${formItem.prop}video`], [0, "srcObject"], "")) {
let tracks = this.$refs[`${formItem.prop}video`][0].srcObject.getVideoTracks()
tracks.forEach(track => {
if (track.getCapabilities().focusMode && track.getCapabilities().focusMode.includes('continuous')) {
track.applyConstraints({ advanced: [{ focusMode: 'continuous' }] });
} else {
ElMessage({
type: 'error',
message: '摄像头不支持连续聚焦模式。',
})
}
});
} else {
ElMessage({
type: 'error',
message: '摄像头不支持连续聚焦模式。',
})
}
}
},
methods: {},
async mounted() { },
watch: {},
computed: {}

29
src/renderer/src/components/tablecomponent.vue

@ -17,13 +17,6 @@
<template v-else-if="headerItem.type === 'date'">
<span>{{ dayjs(row[headerItem.prop]).format("YYYY-MM-DD HH:mm:ss") }}</span>
</template>
<template v-else-if="headerItem.type === 'photo'">
<span v-if="row[headerItem.prop]">
<el-image style="width: 45px" :src="row[headerItem.prop]" :zoom-rate="1.2" :max-scale="7"
:min-scale="0.2" :preview-src-list="[row[headerItem.prop]]" :initial-index="4" fit="cover" />
</span>
<span v-else></span>
</template>
<template v-else>{{ row[headerItem.prop] }}</template>
</template>
</vxe-column>
@ -49,18 +42,16 @@
</vxe-table>
</template>
</el-auto-resizer>
</div>
</template>
<script>
import _ from 'lodash'
import dayjs from 'dayjs'
import { ElMessage, ElMessageBox } from 'element-plus'
export default {
name: 'tablecomponent',
components: {},
emits: ["selectChange", "edit", "info", "del", "headerSort"],
emits: ["selectChange", "edit", "info", "del"],
props: {
tableHeader: {
type: Array,
@ -105,24 +96,6 @@ export default {
optClick(row, type) {
this.$emit(type, row)
},
headerSort(headerItem) {
for (let i = 0; i < this.tableHeader.length; i++) {
let item = this.tableHeader[i];
if (headerItem.prop !== item.prop) {
item.sort = ""
}
}
let sortOld = headerItem.sort
if (sortOld === "") {
headerItem.sort = 1
} else if (sortOld === 1) {
headerItem.sort = -1
} else if (sortOld === -1) {
headerItem.sort = ""
}
this.$emit("headerSort", headerItem)
},
},
async mounted() { },
watch: {},

110
src/renderer/src/views/shujudui.vue → src/renderer/src/views/sale.vue

@ -32,30 +32,12 @@
<span>导入</span>
</el-button>
</el-upload>
<el-button type="primary" @click="exportData" size="large">
<el-icon>
<Download />
</el-icon>
<span>导入示例模板</span>
</el-button>
</el-col>
<el-col :span="6">
<el-input v-model="searchParams.name" placeholder="请输入输入名称" size="large" clearable>
<template #append>
<el-button type="primary" @click="updateSeach()" size="large">
<el-icon>
<Search />
</el-icon>
<span>搜索</span>
</el-button>
</template>
</el-input>
</el-col>
</el-row>
</div>
<div v-loading="loading">
<tablecomponent :tableHeader="tableHeader" :tableData="tableData" @selectChange="selectChange" @edit="edit"
@info="info" @del="del" ref="tableComponentRef" @headerSort="headerSort">
@info="info" @del="del" ref="tableComponentRef">
</tablecomponent>
</div>
<el-dialog v-model="dialogFrom.visible" :title="dialogFrom.title" width="80%" :close-on-click-modal="false">
@ -75,14 +57,14 @@
<script>
import _ from 'lodash'
import * as XLSX from 'xlsx';
import tableHeaderLocal from '../assets/json/shujudui.json'
import tableHeaderLocal from '../assets/json/sale.json'
import { myDatabase } from '../assets/js/db.js'
import dayjs from 'dayjs'
import tablecomponent from "../components/tablecomponent.vue"
import formcomponent from "../components/formcomponent.vue"
import { ElMessage, ElMessageBox } from 'element-plus'
export default {
name: 'shujudui',
name: 'sale',
components: { tablecomponent, formcomponent },
watch: {},
computed: {},
@ -93,9 +75,6 @@ export default {
tableHeader: [],
formHeader: [],
tableData: [],
searchParams: {
name: ""
},
selectionData: [],
dialogFrom: {
visible: false,
@ -127,33 +106,49 @@ export default {
let file = opts.file
this.fileDealData = []
let fileReader = new FileReader()
let dealDataObj = {
"销售数量": 1.2,
"销售金额": 1.2,
"数量小计": 1.2,
"金额小计": 1.2,
}
fileReader.onload = async function () {
let data = this.result
let workbook = XLSX.read(data, { type: 'binary' })
let sheetName = workbook.SheetNames[0]
let sheetData = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName])
console.log(132, sheetData);
let allList = []
let dealData = []
for (let i = 0; i < sheetData.length; i++) {
let element = sheetData[i];
let dealItem = {
name: element["名称"],
data: element["数据堆"],
note: element["备注"],
let item = {}
for (let key in element) {
item[_.trim(key)] = _.trim(element[key])
}
try {
console.log(138, dealItem);
await myDatabase.shujudui.add({
...dealItem,
if (item["行号"] && item["品名"] && item["供应商名称"]) {
for (let key in item) {
if (dealDataObj[_.trim(key)]) {
item[_.trim(key)] = (Number(_.trim(element[key])) * dealDataObj[_.trim(key)]).toFixed(2)
}
dealData.push(item[_.trim(key)])
}
item["allInfo"] = `${_.join(dealData, ",")}`
allList.push({
...item,
create_at: dayjs().format('YYYY-MM-DD HH:mm:ss'),
update_at: dayjs().format('YYYY-MM-DD HH:mm:ss')
})
} catch (error) {
ElMessage({
message: `${dealItem.name}(${dealItem.data})数据堆重复,不可再次新增该数据堆信息`,
type: 'error',
})
}
}
try {
await myDatabase.sale.bulkAdd(allList)
} catch (error) {
ElMessage({
message: `销售数据重复,error:${error}`,
type: 'error',
})
}
await that.updateSeach()
}
fileReader.onerror = function (error) {
@ -164,38 +159,12 @@ export default {
}
fileReader.readAsArrayBuffer(file)
},
//
async exportData() {
let lilstLocal = [{
"名称": "测试1",
"数据堆": "25 78 96 87 21 23",
"备注": "备注1"
}]
let jsonWorkSheet = XLSX.utils.json_to_sheet(lilstLocal);
let workBook = {
SheetNames: ["sheet1"],
Sheets: {
["sheet1"]: jsonWorkSheet,
}
};
XLSX.writeFile(workBook, `导出示例.xlsx`);
},
//
async updateSeach() {
this.loading = true
let allShujuduiList = _.cloneDeep(await myDatabase.shujudui.toArray())
let allSaleList = _.cloneDeep(await myDatabase.sale.toArray())
let dataTemp = []
if (_.trim(this.searchParams.name)) {
for (let i = 0; i < allShujuduiList.length; i++) {
let element = allShujuduiList[i];
if (element.name.includes(_.trim(this.searchParams.name))) {
dataTemp.push({ ...element })
}
}
} else {
dataTemp = _.cloneDeep(allShujuduiList)
}
dataTemp = _.cloneDeep(allSaleList)
let dataTempSort = dataTemp
if (_.get(this.sortObj, ["sort"], "")) {
//
@ -204,11 +173,6 @@ export default {
this.tableData = _.uniqBy(dataTempSort, "id")
this.loading = false
},
//
async headerSort(headerSort) {
this.sortObj = headerSort
await this.updateSeach()
},
//
selectChange(selection) {
this.selectionData = _.cloneDeep(selection)
@ -227,7 +191,7 @@ export default {
async submitDialog() {
let params = { ...this.dialogFrom.formData, }
try {
await myDatabase.shujudui[this.dialogFrom.type]({ ...params, update_at: dayjs().format('YYYY-MM-DD HH:mm:ss') })
await myDatabase.sale[this.dialogFrom.type]({ ...params, update_at: dayjs().format('YYYY-MM-DD HH:mm:ss') })
await this.updateSeach()
this.cancelDialog()
} catch (e) {
@ -306,7 +270,7 @@ export default {
)
.then(async () => {
this.$refs.tableComponentRef.clearSelection()
await myDatabase.shujudui.bulkDelete(idList)
await myDatabase.sale.bulkDelete(idList)
await this.updateSeach()
})
.catch((err) => {
@ -330,7 +294,7 @@ export default {
)
.then(async () => {
this.$refs.tableComponentRef.clearSelection()
await myDatabase.shujudui.clear()
await myDatabase.sale.clear()
await this.updateSeach()
})
.catch(() => {

267
src/renderer/src/views/shujucal.vue

@ -1,267 +0,0 @@
<template>
<div class="rightClass">
<el-row :gutter="20">
<el-col :span="12">
<el-input v-model="dataInput" minlength="5" maxlength="5" placeholder="请输入数字" clearable show-word-limit />
</el-col>
<el-col :span="8">
<el-button type="primary" @click="calNumber">计算</el-button>
<el-button type="primary" @click="clearNumber">清空</el-button>
</el-col>
<!-- <el-col :span="24" style="margin-top: 8px;">
<el-row>
<el-col :span="4">输入随机数: </el-col>
<el-col :span="20">
<el-input v-model="genDataStr" disabled />
</el-col>
</el-row>
</el-col>
<el-col :span="24" style="margin-top: 8px;">
<el-row>
<el-col :span="4">输入随机数记录</el-col>
<el-col :span="20">
<el-input v-model="genDataHistoryStr" disabled :autosize="{ minRows: 2, maxRows: 8 }" type="textarea"
id="textareaInput" />
</el-col>
</el-row>
</el-col> -->
</el-row>
<el-row style="height: calc(100vh - 50px);overflow-y: auto;display: block;">
<el-col v-for="(item, index) in calList" :key="index" style="margin: 8px 0;">
{{ _.join(_.map(item.data, o => (o.name + "共" + o.num + "次")), "、") }}{{ item.str }}
</el-col>
</el-row>
</div>
</template>
<script>
import _ from 'lodash'
import { myDatabase } from '../assets/js/db.js'
import dayjs from 'dayjs'
import { ElMessage, ElMessageBox } from 'element-plus'
export default {
name: 'shujucal',
components: {},
watch: {},
computed: {},
data() {
return {
_: _,
dayjs: dayjs,
dataInput: "",
dataInputLIst: [],
allList: [],
calList: [],
genDataStr: "",
genDataHistoryStr: "",
}
},
methods: {
async getDataList() {
let allData = await myDatabase.shujudui.toArray()
let allList = []
for (let i = 0; i < allData.length; i++) {
let element = allData[i];
let elementList = element.data.split(/[,,\s+]/)
elementList = _.compact(_.uniq(elementList))
allList.push({
id: element.id,
name: element.name,
data: elementList,
note: element.note,
create_at: element.create_at,
update_at: element.update_at,
})
}
this.allList = allList
},
async calNumber() {
await this.getDataList()
if (!this.dataInput) {
ElMessage({
type: "error",
message: "请输入数字"
})
return
}
if (_.isEmpty(this.allList)) {
ElMessage({
type: "error",
message: "请先设置数据"
})
return
}
this.dataInputLIst.push(this.dataInput)
let newList = []
let list = ["万", "千", "百", "十", "个"]
for (let s = 0; s < this.dataInputLIst.length; s++) {
let dataInputItem = this.dataInputLIst[s];
let currentList = dataInputItem.split("")
for (let i = 0; i < currentList.length; i++) {
for (let j = i + 1; j < currentList.length; j++) {
newList.push({
num: `${currentList[i]}${currentList[j]}`,
lable: `${list[i]}${list[j]}`,
numlable: `${list[i]}${list[j]}`
})
}
}
}
let newListTemp = JSON.parse(JSON.stringify(newList))
let newListTempLocal = []
for (let i = 0; i < newListTemp.length; i++) {
// let element = newListTemp[i];
let element = {}
for (let s = this.allList.length - 1; s >= 0; s--) {
let item = JSON.parse(JSON.stringify(this.allList[s]))
element["numlable"] = newListTemp[i].numlable
element["num"] = newListTemp[i].num
element["id"] = item.id
element["name"] = item.name
element["data"] = [...item.data]
element["dataStr"] = item.data.join("_")
element["dataStrFirstGroup"] = `${item.data.join("_")}${newListTemp[i].numlable}`
element["note"] = item.note
element["create_at"] = item.create_at
element["update_at"] = item.update_at
if (_.includes(item.data, element.num)) {
element["has"] = true
} else {
element["has"] = false
}
newListTempLocal.push({ ...element })
}
}
let groupName = _.groupBy(newListTempLocal, "dataStrFirstGroup")
let newLIstTemp = []
for (let key in groupName) {
let value = groupName[key]
let filterData = _.filter(value.slice(value.length - 5), { has: true })
if (filterData.length === 0) {
newLIstTemp = newLIstTemp.concat(value)
}
}
let allGroup = _.groupBy(newLIstTemp, "dataStr")
let allListTemp = []
for (let key in allGroup) {
let value = allGroup[key]
let perGroup = _.groupBy(value, "numlable")
let textObj = {
data: [],
str: ""
}
for (let key1 in perGroup) {
let element = perGroup[key1];
let has = _.get(element, [element.length - 1, "has"], false) || _.get(element, [element.length - 2, "has"], false) || _.get(element, [element.length - 3, "has"], false) || _.get(element, [element.length - 4, "has"], false) || _.get(element, [element.length - 5, "has"], false)
let hasIndex = _.findLastIndex(element, { has: true })
textObj.data.push({
name: key1,
has: has,
num: element.length - hasIndex - 1
})
}
textObj.str = value[0].data.join(" ")
allListTemp.push(textObj)
}
let allListGroup = _.groupBy(allListTemp, "str")
this.calList = []
for (let key in allListGroup) {
let value = allListGroup[key];
let mergedData = [];
for (let i = 0; i < value.length; i++) {
let valueItem = value[i];
for (let j = 0; j < valueItem.data.length; j++) {
let dataItem = valueItem.data[j];
let mergedDataTemp = _.cloneDeep(mergedData)
let findItem = _.find(mergedDataTemp, item => item.name === dataItem.name)
if (findItem) {
findItem.num++
if (dataItem.has) {
findItem.num = 0
}
} else {
mergedData.push(dataItem)
}
}
}
mergedData = _.filter(mergedData, o => o.num > 5)
if (mergedData.length) {
this.calList.push({
data: mergedData,
str: key
})
}
}
for (let i = 0; i < this.calList.length; i++) {
let element = this.calList[i];
let eleItem = _.find(this.allList, { str: element.data })
if (eleItem) {
element["id"] = eleItem.id
}
}
this.calList = _.sortBy(this.calList, "id").reverse()
this.genData()
// this.genDataHistory()
},
clearNumber() {
this.dataInput = ""
this.dataInputLIst = []
this.calList = []
this.genData()
// this.genDataHistory()
},
genData() {
let newList = ""
for (let i = 0; i < this.dataInput.length; i++) {
for (let j = i + 1; j < this.dataInput.length; j++) {
newList += `${this.dataInput[i]}${this.dataInput[j]} `
}
}
this.genDataStr = newList
},
genDataHistory() {
let that = this
let item = ""
for (let s = 0; s < this.dataInputLIst.length; s++) {
let dataInputItem = this.dataInputLIst[s];
let currentList = dataInputItem.split("")
item += `${s + 1}: `
for (let i = 0; i < currentList.length; i++) {
for (let j = i + 1; j < currentList.length; j++) {
item += `${currentList[i]}${currentList[j]} `
}
}
item += `\n`
}
this.genDataHistoryStr = item
let textareaItem = document.getElementById("textareaInput");
textareaItem.scrollTop = textareaItem.scrollHeight
}
},
async mounted() {
},
}
</script>
<style scoped>
.rightClass {
padding: 8px;
}
.uploadClass {
top: 3px;
}
.inputClass {
display: inline-block;
margin: 0 8px;
position: relative;
}
.tagClass {
display: inline-block;
margin: 0 8px;
width: 200px;
position: relative;
}
</style>
Loading…
Cancel
Save