Commit d8ce7fa2 by qinjianhui

Merge branch 'dev' into 'master'

Dev

See merge request !290
parents c90fd5ad a690a9b4
...@@ -12,15 +12,20 @@ declare module 'vue' { ...@@ -12,15 +12,20 @@ declare module 'vue' {
DatePicker: typeof import('./src/components/Form/DatePicker.vue')['default'] DatePicker: typeof import('./src/components/Form/DatePicker.vue')['default']
DateRangePicker: typeof import('./src/components/Form/DateRangePicker.vue')['default'] DateRangePicker: typeof import('./src/components/Form/DateRangePicker.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton'] ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCarousel: typeof import('element-plus/es')['ElCarousel'] ElCarousel: typeof import('element-plus/es')['ElCarousel']
ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem'] ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem']
ElCascader: typeof import('element-plus/es')['ElCascader']
ElCascaderPanel: typeof import('element-plus/es')['ElCascaderPanel']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElCol: typeof import('element-plus/es')['ElCol'] ElCol: typeof import('element-plus/es')['ElCol']
ElCollapse: typeof import('element-plus/es')['ElCollapse'] ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog'] ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElDrawer: typeof import('element-plus/es')['ElDrawer'] ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown'] ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
...@@ -29,20 +34,33 @@ declare module 'vue' { ...@@ -29,20 +34,33 @@ declare module 'vue' {
ElForm: typeof import('element-plus/es')['ElForm'] ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElIcon: typeof import('element-plus/es')['ElIcon'] ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput'] ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElOption: typeof import('element-plus/es')['ElOption'] ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination'] ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopover: typeof import('element-plus/es')['ElPopover'] ElPopover: typeof import('element-plus/es')['ElPopover']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow'] ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect'] ElSelect: typeof import('element-plus/es')['ElSelect']
ElStep: typeof import('element-plus/es')['ElStep']
ElSteps: typeof import('element-plus/es')['ElSteps']
ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable'] ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag'] ElTag: typeof import('element-plus/es')['ElTag']
ElTimeline: typeof import('element-plus/es')['ElTimeline']
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElTree: typeof import('element-plus/es')['ElTree']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
ElUpload: typeof import('element-plus/es')['ElUpload']
Icon: typeof import('./src/components/Icon.vue')['default'] Icon: typeof import('./src/components/Icon.vue')['default']
ImageView: typeof import('./src/components/ImageView.vue')['default'] ImageView: typeof import('./src/components/ImageView.vue')['default']
LeftRightLayout: typeof import('./src/components/leftRightLayout.vue')['default'] LeftRightLayout: typeof import('./src/components/leftRightLayout.vue')['default']
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
"bignumber.js": "^9.3.0", "bignumber.js": "^9.3.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"echarts": "^6.0.0", "echarts": "^6.0.0",
"element-plus": "^2.6.0", "element-plus": "^2.14.3",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"luxon": "^3.7.1", "luxon": "^3.7.1",
"pinia": "^2.1.7", "pinia": "^2.1.7",
...@@ -510,18 +510,18 @@ ...@@ -510,18 +510,18 @@
} }
}, },
"node_modules/@ctrl/tinycolor": { "node_modules/@ctrl/tinycolor": {
"version": "3.6.1", "version": "4.2.0",
"resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-4.2.0.tgz",
"integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", "integrity": "sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=10" "node": ">=14"
} }
}, },
"node_modules/@element-plus/icons-vue": { "node_modules/@element-plus/icons-vue": {
"version": "2.3.1", "version": "2.3.2",
"resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz", "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz",
"integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==", "integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==",
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
"vue": "^3.2.0" "vue": "^3.2.0"
...@@ -1022,28 +1022,28 @@ ...@@ -1022,28 +1022,28 @@
} }
}, },
"node_modules/@floating-ui/core": { "node_modules/@floating-ui/core": {
"version": "1.7.0", "version": "1.8.0",
"resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.0.tgz", "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.8.0.tgz",
"integrity": "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA==", "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@floating-ui/utils": "^0.2.9" "@floating-ui/utils": "^0.2.12"
} }
}, },
"node_modules/@floating-ui/dom": { "node_modules/@floating-ui/dom": {
"version": "1.7.0", "version": "1.8.0",
"resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.0.tgz", "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.8.0.tgz",
"integrity": "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg==", "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@floating-ui/core": "^1.7.0", "@floating-ui/core": "^1.8.0",
"@floating-ui/utils": "^0.2.9" "@floating-ui/utils": "^0.2.12"
} }
}, },
"node_modules/@floating-ui/utils": { "node_modules/@floating-ui/utils": {
"version": "0.2.9", "version": "0.2.12",
"resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.9.tgz", "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.12.tgz",
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@humanwhocodes/config-array": { "node_modules/@humanwhocodes/config-array": {
...@@ -1624,9 +1624,9 @@ ...@@ -1624,9 +1624,9 @@
}, },
"node_modules/@popperjs/core": { "node_modules/@popperjs/core": {
"name": "@sxzz/popperjs-es", "name": "@sxzz/popperjs-es",
"version": "2.11.7", "version": "2.11.8",
"resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.8.tgz",
"integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==", "integrity": "sha512-wOwESXvvED3S8xBmcPWHs2dUuzrE4XiZeFu7e1hROIJkm02a49N120pmOXxY33sBb6hArItm5W5tcg1cBtV+HQ==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
...@@ -1983,9 +1983,9 @@ ...@@ -1983,9 +1983,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/lodash": { "node_modules/@types/lodash": {
"version": "4.17.17", "version": "4.17.24",
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.17.tgz", "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.24.tgz",
"integrity": "sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==", "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/lodash-es": { "node_modules/@types/lodash-es": {
...@@ -2075,9 +2075,9 @@ ...@@ -2075,9 +2075,9 @@
"optional": true "optional": true
}, },
"node_modules/@types/web-bluetooth": { "node_modules/@types/web-bluetooth": {
"version": "0.0.16", "version": "0.0.21",
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz", "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==", "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
...@@ -2828,39 +2828,41 @@ ...@@ -2828,39 +2828,41 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@vueuse/core": { "node_modules/@vueuse/core": {
"version": "9.13.0", "version": "14.3.0",
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz", "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-14.3.0.tgz",
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==", "integrity": "sha512-aHfz47g0ZhMtTVHmIzMVpJy8ePhhOy68GY5bv110+5DVtZ+W7BsOx+m61UNQqfrWyPztIHIanWa3E2tib3NFIw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@types/web-bluetooth": "^0.0.16", "@types/web-bluetooth": "^0.0.21",
"@vueuse/metadata": "9.13.0", "@vueuse/metadata": "14.3.0",
"@vueuse/shared": "9.13.0", "@vueuse/shared": "14.3.0"
"vue-demi": "*"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/antfu" "url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"vue": "^3.5.0"
} }
}, },
"node_modules/@vueuse/metadata": { "node_modules/@vueuse/metadata": {
"version": "9.13.0", "version": "14.3.0",
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz", "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-14.3.0.tgz",
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==", "integrity": "sha512-BwxmbAzwAVF50+MW57GXOUEV61nFBGnlBvrTqj49PqWJu3uw7hdu72ztXeZ33RdZtDY6kO+bfCAE1PCn88Tktw==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"url": "https://github.com/sponsors/antfu" "url": "https://github.com/sponsors/antfu"
} }
}, },
"node_modules/@vueuse/shared": { "node_modules/@vueuse/shared": {
"version": "9.13.0", "version": "14.3.0",
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz", "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-14.3.0.tgz",
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==", "integrity": "sha512-bZpge9eSXwa4ToSiqJ7j6KRwhAsneMFoSz3LMWKQDkqimm3D/tbFlrklrs/IOqC8tEcYmXQZJ6N0UrjhBirVCg==",
"license": "MIT", "license": "MIT",
"dependencies": {
"vue-demi": "*"
},
"funding": { "funding": {
"url": "https://github.com/sponsors/antfu" "url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"vue": "^3.5.0"
} }
}, },
"node_modules/@vxe-ui/core": { "node_modules/@vxe-ui/core": {
...@@ -3509,9 +3511,9 @@ ...@@ -3509,9 +3511,9 @@
} }
}, },
"node_modules/dayjs": { "node_modules/dayjs": {
"version": "1.11.13", "version": "1.11.21",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.13.tgz", "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.21.tgz",
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/de-indent": { "node_modules/de-indent": {
...@@ -3665,29 +3667,29 @@ ...@@ -3665,29 +3667,29 @@
"license": "ISC" "license": "ISC"
}, },
"node_modules/element-plus": { "node_modules/element-plus": {
"version": "2.9.11", "version": "2.14.3",
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.9.11.tgz", "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.14.3.tgz",
"integrity": "sha512-x4L/6YC8de4JtuE3vpaEugJdQIeHQaHtIYKyk67IeF6dTIiVax45aX4nWOygnh+xX+0gTvL6xO+9BZhPA3G82w==", "integrity": "sha512-pJcvxcpZjYruNzuJhAeVwnbYjfNgzBKnWHwSVEhwzM2/kcLI3brzmtIBxtPqd4hQWJfD1PRnjoc1WipLw2eBGg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@ctrl/tinycolor": "^3.4.1", "@ctrl/tinycolor": "^4.2.0",
"@element-plus/icons-vue": "^2.3.1", "@element-plus/icons-vue": "^2.3.2",
"@floating-ui/dom": "^1.0.1", "@floating-ui/dom": "^1.7.6",
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.8",
"@types/lodash": "^4.14.182", "@types/lodash": "^4.17.24",
"@types/lodash-es": "^4.17.6", "@types/lodash-es": "^4.17.12",
"@vueuse/core": "^9.1.0", "@vueuse/core": "14.3.0",
"async-validator": "^4.2.5", "async-validator": "^4.2.5",
"dayjs": "^1.11.13", "dayjs": "^1.11.20",
"escape-html": "^1.0.3", "lodash": "^4.18.1",
"lodash": "^4.17.21", "lodash-es": "^4.18.1",
"lodash-es": "^4.17.21", "lodash-unified": "^1.0.3",
"lodash-unified": "^1.0.2",
"memoize-one": "^6.0.0", "memoize-one": "^6.0.0",
"normalize-wheel-es": "^1.2.0" "normalize-wheel-es": "^1.2.0",
"vue-component-type-helpers": "^3.3.5"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "^3.2.0" "vue": "^3.3.7"
} }
}, },
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
...@@ -3842,12 +3844,6 @@ ...@@ -3842,12 +3844,6 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"license": "MIT"
},
"node_modules/escape-string-regexp": { "node_modules/escape-string-regexp": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
...@@ -4940,15 +4936,15 @@ ...@@ -4940,15 +4936,15 @@
} }
}, },
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.21", "version": "4.18.1",
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash-es": { "node_modules/lodash-es": {
"version": "4.17.21", "version": "4.18.1",
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.18.1.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/lodash-unified": { "node_modules/lodash-unified": {
...@@ -6599,6 +6595,12 @@ ...@@ -6599,6 +6595,12 @@
} }
} }
}, },
"node_modules/vue-component-type-helpers": {
"version": "3.3.7",
"resolved": "https://registry.npmmirror.com/vue-component-type-helpers/-/vue-component-type-helpers-3.3.7.tgz",
"integrity": "sha512-Skkhw9agYSgsWqv7bxSOGJZa9SaiJbZVGdXuFWnrzKaQYHnw9qbjD630rw6RyMqDbp54nfLCLw5SZA55if7JLg==",
"license": "MIT"
},
"node_modules/vue-demi": { "node_modules/vue-demi": {
"version": "0.14.10", "version": "0.14.10",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
"bignumber.js": "^9.3.0", "bignumber.js": "^9.3.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"echarts": "^6.0.0", "echarts": "^6.0.0",
"element-plus": "^2.6.0", "element-plus": "^2.14.3",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"luxon": "^3.7.1", "luxon": "^3.7.1",
"pinia": "^2.1.7", "pinia": "^2.1.7",
......
...@@ -74,7 +74,6 @@ export function getFactoryOrderNewListApi( ...@@ -74,7 +74,6 @@ export function getFactoryOrderNewListApi(
/** 请求体与 `factory/podOrder/list_page` 一致 */ /** 请求体与 `factory/podOrder/list_page` 一致 */
export function getPodOrderAcceptedStatisticsApi( export function getPodOrderAcceptedStatisticsApi(
data: SearchForm, data: SearchForm,
currentPage: number,
pageSize: number, pageSize: number,
status?: string, status?: string,
subStatus?: number, subStatus?: number,
...@@ -88,7 +87,7 @@ export function getPodOrderAcceptedStatisticsApi( ...@@ -88,7 +87,7 @@ export function getPodOrderAcceptedStatisticsApi(
}> }>
>('factory/podOrder/acceptedStatistics', { >('factory/podOrder/acceptedStatistics', {
...normalizePodOrderQueryPayload({ ...data } as Record<string, unknown>), ...normalizePodOrderQueryPayload({ ...data } as Record<string, unknown>),
currentPage, currentPage: 1,
pageSize, pageSize,
status, status,
acceptedStatus: subStatus, acceptedStatus: subStatus,
......
...@@ -11,7 +11,11 @@ import { AddDeclarationRuleObj } from '@/views/logistics/types/declarationRule' ...@@ -11,7 +11,11 @@ import { AddDeclarationRuleObj } from '@/views/logistics/types/declarationRule'
import { LogisticsQuotation } from '@/views/logistics/types/logisticsQuotation' import { LogisticsQuotation } from '@/views/logistics/types/logisticsQuotation'
import { LogisticsPartitionObj } from '@/views/logistics/types/logisticsPartition' import { LogisticsPartitionObj } from '@/views/logistics/types/logisticsPartition'
import { ShippingAddressObj } from '@/views/logistics/types/shippingAddress' import { ShippingAddressObj } from '@/views/logistics/types/shippingAddress'
import { IsortingInfo } from '@/types/api/logistics' import {
IsortingInfo,
SortingPortAreaItem,
SortingPortOptionItem,
} from '@/types/api/logistics'
export interface ILogisticsList { export interface ILogisticsList {
code: string code: string
basicsName: string basicsName: string
...@@ -168,9 +172,12 @@ export function getLogisticsWayListByCompanyId(companyId: number) { ...@@ -168,9 +172,12 @@ export function getLogisticsWayListByCompanyId(companyId: number) {
} }
// 获取tictok物流承运商 // 获取tictok物流承运商
export function getTiktokCarrier() { export function getTiktokCarrier() {
return axios.get<never, BaseRespData<{ name: string; id: string, typeCode: string, label: string }[]>>( return axios.get<
'logisticsWay/getTiktokShippingProvider', never,
) BaseRespData<
{ name: string; id: string; typeCode: string; label: string }[]
>
>('logisticsWay/getTiktokShippingProvider')
} }
/** /**
...@@ -461,16 +468,14 @@ export function logisticsCompanyAllCodelist() { ...@@ -461,16 +468,14 @@ export function logisticsCompanyAllCodelist() {
'/logisticsCompany/allCodelist', '/logisticsCompany/allCodelist',
) )
} }
/** // 物流 分拣配置
分拣配置
*/
export function getsortingConfigListApi( export function getsortingConfigListApi(
data: IsortingInfo, data: IsortingInfo,
currentPage: number, currentPage: number,
pageSize: number, pageSize: number,
) { ) {
return axios.post<never, BasePaginationData<IsortingInfo>>( return axios.post<never, BasePaginationData<IsortingInfo>>(
'logistics/sortingConfig/list_page', 'logistics-sort-port-config/list_page',
{ {
...data, ...data,
currentPage, currentPage,
...@@ -481,34 +486,52 @@ export function getsortingConfigListApi( ...@@ -481,34 +486,52 @@ export function getsortingConfigListApi(
// 详情 // 详情
export function getSortingConfig(params: { id: number | string }) { export function getSortingConfig(params: { id: number | string }) {
return axios.get<never, BaseRespData<IsortingInfo>>( return axios.get<never, BaseRespData<IsortingInfo>>(
'logistics/sortingConfig/get', 'logistics-sort-port-config/get',
{ params }, { params },
) )
} }
export function createSortingApi(data: IsortingInfo) { export function createSortingApi(data: IsortingInfo) {
return axios.post<never, BaseRespData<never>>( return axios.post<never, BaseRespData<never>>(
'logistics/sortingConfig/add', 'logistics-sort-port-config/add',
data, data,
) )
} }
export function updateSortingApi(data: IsortingInfo) { export function updateSortingApi(data: IsortingInfo) {
return axios.post<never, BaseRespData<never>>( return axios.post<never, BaseRespData<never>>(
'logistics/sortingConfig/update', 'logistics-sort-port-config/update',
data, data,
) )
} }
export function deleteSortingApi(params: { export function deleteSortingApi(params: { id: number }) {
ids: string
type: string | number
}) {
return axios.get<never, BaseRespData<never>>( return axios.get<never, BaseRespData<never>>(
'logistics/sortingConfig/delete', 'logistics-sort-port-config/delete',
{ {
params, params,
}, },
) )
} }
// 分拣配置 查询可用分拣口
export function getSortingPortAreaListApi() {
return axios.get<never, BaseRespData<SortingPortAreaItem[]>>(
'logistics-sort-port-config/area-list',
)
}
// 分拣口规则 条件类型
export function getSortPortConditionTypeListApi() {
return axios.get<never, BaseRespData<SortingPortOptionItem[]>>(
'logistics-sort-port-config/condition-type',
)
}
// 分拣口规则 操作符
export function getSortPortOperatorListApi() {
return axios.get<never, BaseRespData<SortingPortOptionItem[]>>(
'logistics-sort-port-config/operator-list',
)
}
// 物流跟踪 获取菜单树 // 物流跟踪 获取菜单树
export function logisticStatusList() { export function logisticStatusList() {
return axios.get<never, BaseRespData<LogisticsTrackingTree[]>>( return axios.get<never, BaseRespData<LogisticsTrackingTree[]>>(
......
...@@ -15,6 +15,8 @@ import { ...@@ -15,6 +15,8 @@ import {
SortingList, SortingList,
SortingRuleDetail, SortingRuleDetail,
OperatorList, OperatorList,
FactoryOrderFlowConfigItem,
FactoryOrderFlowConfig,
} from '@/types/api/order' } from '@/types/api/order'
import { import {
apiSubmitPodOrderForm, apiSubmitPodOrderForm,
...@@ -780,3 +782,31 @@ export function getSortingLogApi(id: number) { ...@@ -780,3 +782,31 @@ export function getSortingLogApi(id: number) {
params: { id }, params: { id },
}) })
} }
// 订单流程配置 列表
export function listFactoryOrderFlowConfigApi() {
return axios.get<never, BaseRespData<FactoryOrderFlowConfigItem[]>>(
'report/factory-order-flow-config/list-flow',
)
}
// 订单流程配置 更新
export function updateFactoryOrderFlowConfigApi(data: FactoryOrderFlowConfig) {
return axios.post<never, BaseRespData<never>>(
'report/factory-order-flow-config/update',
data,
)
}
// 订单流程配置 获取质检状态
export function getFactoryOrderFlowConfigApi() {
return axios.get<never, BaseRespData<FactoryOrderFlowConfig>>(
'report/factory-order-flow-config/get-config',
)
}
// 配货分拣 商品类目
export function getCategoryListApi() {
return axios.get<never, BaseRespData<OperatorList[]>>(
'allocation-sorting-rule/find-custom-category-list',
)
}
...@@ -126,11 +126,11 @@ const attrs = useAttrs() ...@@ -126,11 +126,11 @@ const attrs = useAttrs()
const slots = useSlots() as Record<string, Slot> const slots = useSlots() as Record<string, Slot>
const setCurrentRow = (row: T) => { const setCurrentRow = (row: T) => {
tableRef.value?.setCurrentRow(row) tableRef.value?.setCurrentRow(row as Record<PropertyKey, any>)
} }
const toggleRowSelection = (row: T, selected: boolean = true) => { const toggleRowSelection = (row: T, selected: boolean = true) => {
tableRef.value?.toggleRowSelection(row, selected) tableRef.value?.toggleRowSelection(row as Record<PropertyKey, any>, selected)
} }
const clearSelection = () => { const clearSelection = () => {
...@@ -139,11 +139,11 @@ const clearSelection = () => { ...@@ -139,11 +139,11 @@ const clearSelection = () => {
const toggleAllSelection = () => { const toggleAllSelection = () => {
tableRef.value?.toggleAllSelection() tableRef.value?.toggleAllSelection()
} }
const rowClick = (row:NonNullable<unknown>)=>{ const rowClick = (row: NonNullable<unknown>) => {
console.log(row) console.log(row)
console.log(tableRef.value,props.rowClickSelect) console.log(tableRef.value, props.rowClickSelect)
if(props.rowClickSelect){ if (props.rowClickSelect) {
tableRef.value?.toggleRowSelection(row,true) tableRef.value?.toggleRowSelection(row as Record<PropertyKey, any>, true)
} }
} }
const handleTableHeaderClick = (column: { type?: string }, event: Event) => { const handleTableHeaderClick = (column: { type?: string }, event: Event) => {
...@@ -170,7 +170,10 @@ const selectAllRows = (select: boolean, setInternalIsMore?: boolean) => { ...@@ -170,7 +170,10 @@ const selectAllRows = (select: boolean, setInternalIsMore?: boolean) => {
nextTick(() => { nextTick(() => {
if (tableRef.value && props.paginatedData.length > 0) { if (tableRef.value && props.paginatedData.length > 0) {
props.paginatedData.forEach((row) => { props.paginatedData.forEach((row) => {
tableRef.value?.toggleRowSelection(row, select) tableRef.value?.toggleRowSelection(
row as Record<PropertyKey, any>,
select,
)
}) })
} }
......
...@@ -4,6 +4,7 @@ import App from './App.vue' ...@@ -4,6 +4,7 @@ import App from './App.vue'
import router, { registerDynamicRoutes } from './router' import router, { registerDynamicRoutes } from './router'
import store from './store' import store from './store'
import './styles/index.scss' import './styles/index.scss'
import 'element-plus/es/components/message/style/css'
import roleShow from './utils/roleShow' import roleShow from './utils/roleShow'
import VxeUITable from 'vxe-table' import VxeUITable from 'vxe-table'
import 'vxe-table/lib/style.css' import 'vxe-table/lib/style.css'
...@@ -46,5 +47,4 @@ app ...@@ -46,5 +47,4 @@ app
.use(VxeUITable) .use(VxeUITable)
.mount('#app') .mount('#app')
app.config.globalProperties.$isPermissionBtn = isPermissionBtn app.config.globalProperties.$isPermissionBtn = isPermissionBtn
...@@ -25,6 +25,8 @@ export const dynamicComponentMap: Record<string, LazyComponent> = { ...@@ -25,6 +25,8 @@ export const dynamicComponentMap: Record<string, LazyComponent> = {
import('@/views/order/orderTracking/index.vue'), import('@/views/order/orderTracking/index.vue'),
'/pod-us-order/podUsSchedulingRules': () => '/pod-us-order/podUsSchedulingRules': () =>
import('@/views/order/podUsSchedulingRules/index.vue'), import('@/views/order/podUsSchedulingRules/index.vue'),
'/order/factory-order-flow-config': () =>
import('@/views/order/factoryOrderFlowConfig/index.vue'),
'/order/factory-order-new': () => '/order/factory-order-new': () =>
import('@/views/order/factoryOrderNew/index.vue'), import('@/views/order/factoryOrderNew/index.vue'),
'/order/cancel-order-process': () => '/order/cancel-order-process': () =>
......
...@@ -45,3 +45,23 @@ ...@@ -45,3 +45,23 @@
.el-loading-mask { .el-loading-mask {
z-index: 1020 !important; z-index: 1020 !important;
} }
/* ElMessage:兼容 Vite 预构建旧版 JS(无 is-center)与新版 CSS 的定位差异 */
.el-message {
left: 50%;
transform: translateX(-50%);
}
.el-message.is-left {
left: 16px;
right: auto;
transform: none;
}
.el-message.is-right {
left: auto;
right: 16px;
transform: none;
}
.el-message.is-center {
left: 50%;
transform: translateX(-50%);
}
...@@ -15,11 +15,34 @@ export interface Ilogistics { ...@@ -15,11 +15,34 @@ export interface Ilogistics {
createTime?: string createTime?: string
updateTime?: string | null updateTime?: string | null
} }
export interface SortingPortCondition {
id?: number
ruleId?: number
conditionType: string
operator: string
conditionValue?: string | number | string[]
}
export interface IsortingInfo { export interface IsortingInfo {
id?: number id?: number
sortingFacility?: string | null sortingRemark?: string
sortingArea: number | null sortingArea?: number | null
sortingAreaName?: string | null sortingAreaName?: string | null
serviceCode: string | null matchType?: 1 | 2
type?: string | number sortNo?: number
conditions?: SortingPortCondition[]
conditionsText?: string
}
export interface SortingPortAreaItem {
code: number
desc: string
used: boolean
}
export interface SortingPortOptionItem {
desc: string
code: string
used?: boolean
} }
...@@ -304,6 +304,8 @@ export interface SortingList { ...@@ -304,6 +304,8 @@ export interface SortingList {
warehouseName: string warehouseName: string
autoPrint: boolean autoPrint: boolean
defaultWarehouse: 0 | 1 defaultWarehouse: 0 | 1
matchType: 1 | 2 // 匹配类型:1=所有条件(且) 2=任一条件(或)
conditions: SortingRuleCondition[] // 配货规则
} }
// 配货分拣 详情 // 配货分拣 详情
...@@ -328,7 +330,7 @@ export interface SortingRuleCondition { ...@@ -328,7 +330,7 @@ export interface SortingRuleCondition {
ruleId?: number ruleId?: number
conditionType: string // 条件 conditionType: string // 条件
operator: string // 操作符 operator: string // 操作符
conditionValue?: string | number | string[] conditionValue?: string | number | (string | number)[]
} }
// 配货分拣 配货区/操作符/条件 // 配货分拣 配货区/操作符/条件
...@@ -338,3 +340,16 @@ export interface OperatorList { ...@@ -338,3 +340,16 @@ export interface OperatorList {
used?: boolean used?: boolean
def?: boolean def?: boolean
} }
// 工厂订单流程配置 节点
export interface FactoryOrderFlowConfigItem {
code: string
name: string
sort: number
}
// 工厂订单流程配置 修改
export interface FactoryOrderFlowConfig {
id: number
skipQualityInspect: boolean
}
...@@ -516,9 +516,14 @@ ...@@ -516,9 +516,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ElMessage, ElRadioGroup, ElTree, TableColumnCtx } from 'element-plus' import {
ElMessage,
ElRadioGroup,
ElTree,
TableColumnCtx,
type TableInstance,
} from 'element-plus'
import splitDiv from '@/components/splitDiv/splitDiv.vue' import splitDiv from '@/components/splitDiv/splitDiv.vue'
import { ElTable } from 'element-plus'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
import { useValue } from '@/utils/hooks/useValue' import { useValue } from '@/utils/hooks/useValue'
import { import {
...@@ -554,7 +559,9 @@ interface Tree { ...@@ -554,7 +559,9 @@ interface Tree {
code?: string code?: string
children?: Tree[] children?: Tree[]
} }
interface SummaryMethodProps<T = AccountStatementNote> { interface SummaryMethodProps<
T extends Record<PropertyKey, any> = AccountStatementNote,
> {
columns: TableColumnCtx<T>[] columns: TableColumnCtx<T>[]
data: T[] data: T[]
} }
...@@ -568,7 +575,7 @@ const dateRange = ref<string[]>([]) ...@@ -568,7 +575,7 @@ const dateRange = ref<string[]>([])
const selections = ref<AccountStatementNote[]>([]) const selections = ref<AccountStatementNote[]>([])
const detailList = ref<ItemList[]>([]) const detailList = ref<ItemList[]>([])
const tabsValue = ref<string>('0') const tabsValue = ref<string>('0')
const singleTableRef = ref<InstanceType<typeof ElTable>>() const singleTableRef = ref<TableInstance>()
const currentRow = ref<AccountStatementNote | null>(null) const currentRow = ref<AccountStatementNote | null>(null)
const logList = ref<LogList[]>([]) const logList = ref<LogList[]>([])
...@@ -583,7 +590,7 @@ const { ...@@ -583,7 +590,7 @@ const {
refresh: search, refresh: search,
onCurrentPageChange: handleCurrentChange, onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange, onPageSizeChange: handleSizeChange,
} = usePageList({ } = usePageList<AccountStatementNote>({
query: (page, pageSize) => query: (page, pageSize) =>
customJomallReconciliation( customJomallReconciliation(
{ {
...@@ -623,7 +630,7 @@ const getTreeNum = async () => { ...@@ -623,7 +630,7 @@ const getTreeNum = async () => {
console.error(e) console.error(e)
} }
} }
const rowClick = (row: AccountStatementNote) => { const rowClick = (row: AccountStatementNote | null) => {
if (!row) { if (!row) {
currentRow.value = null currentRow.value = null
} }
...@@ -789,7 +796,7 @@ const submitConfirmOrder = async () => { ...@@ -789,7 +796,7 @@ const submitConfirmOrder = async () => {
search() search()
await getTreeNum() await getTreeNum()
if (singleTableRef.value) { if (singleTableRef.value) {
singleTableRef.value!.setCurrentRow(currentRow.value) singleTableRef.value!.setCurrentRow(currentRow.value ?? undefined)
} }
} }
watch( watch(
......
<template>
<ElDialog
v-model="visible"
:title="isEdit ? '编辑分拣口' : '新增分拣口'"
:close-on-click-modal="false"
width="800px"
destroy-on-close
>
<el-form ref="formRef" label-width="120px" :model="form" :rules="rules">
<el-form-item label="分拣口说明" prop="sortingRemark">
<el-input
v-model="form.sortingRemark"
style="width: 100%"
clearable
maxlength="100"
show-word-limit
placeholder="请输入分拣口说明"
/>
</el-form-item>
<el-form-item label="分拣口" prop="sortingArea">
<el-select
v-model="form.sortingArea"
style="width: 200px"
placeholder="请选择"
>
<el-option
v-for="item in areaList"
:key="item.code"
:label="item.desc"
:value="item.code"
/>
</el-select>
</el-form-item>
<el-form-item label="分拣口规则" prop="conditions">
<div class="sorting-rules">
<div class="header">
<span class="mr-10">满足条件</span>
<el-radio-group v-model="form.matchType">
<el-radio :value="1">满足以下所有条件</el-radio>
<el-radio :value="2">满足以下任一条件</el-radio>
</el-radio-group>
</div>
<div
v-for="(item, index) in form.conditions"
:key="index"
class="content"
>
<el-select
v-model="item.conditionType"
style="width: 160px"
placeholder="请选择分拣口规则"
@change="handleConditionTypeChange(item)"
>
<el-option
v-for="rule in getAvailableConditionTypes(index)"
:key="rule.code"
:label="rule.desc"
:value="rule.code"
/>
</el-select>
<el-select
v-model="item.operator"
style="width: 160px"
placeholder="请选择关系"
>
<el-option
v-for="operator in operatorList"
:key="operator.code"
:label="operator.desc"
:value="operator.code"
/>
</el-select>
<template v-if="item.conditionType === 'order_platform'">
<el-select
v-model="item.conditionValue"
class="platform-select"
style="width: 160px"
placeholder="请选择平台"
filterable
multiple
collapse-tags
collapse-tags-tooltip
:max-collapse-tags="1"
clearable
popper-class="customize-select-style"
>
<el-option
v-for="platform in platformList"
:key="platform.type"
:label="platform.type"
:value="platform.type"
style="width: 160px"
>
<img
:src="`/images/icon/${platform.icon.split('/').pop()}`"
style="height: 20px; margin: 5px 10px 0 0"
/>
<span :title="platform.type">{{ platform.type }}</span>
</el-option>
</el-select>
</template>
<template v-else>
<el-select
v-model="item.conditionValue"
class="platform-select"
style="width: 160px"
placeholder="请选择物流公司"
filterable
multiple
collapse-tags
collapse-tags-tooltip
:max-collapse-tags="1"
clearable
>
<el-option
v-for="logistics in logisticsMethodsList"
:key="logistics.code"
:label="logistics.basicsName"
:value="logistics.code"
/>
</el-select>
</template>
<el-button
v-if="
(form.conditions?.length ?? 0) <
(conditionTypeList?.length ?? 0)
"
type="primary"
class="rule-action-btn"
:icon="Plus"
size="small"
circle
@click="addConditionItem"
/>
<el-button
v-if="(form.conditions?.length ?? 0) > 1"
color="#F56C6C"
class="rule-action-btn"
:icon="Minus"
size="small"
circle
plain
@click="removeConditionItem(index)"
/>
</div>
</div>
</el-form-item>
<el-form-item label="序号" prop="sortNo">
<el-input-number
v-model="form.sortNo"
:min="1"
:max="999"
style="width: 200px"
step-strictly
controls-position="right"
placeholder="请输入序号"
/>
</el-form-item>
<el-form-item>
<span class="item-info">分拣配置逻辑冲突时,优先执行更小的序号</span>
</el-form-item>
</el-form>
<template #footer>
<div class="flex flex-center">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</div>
</template>
</ElDialog>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { Plus, Minus } from '@element-plus/icons-vue'
import type { FormInstance } from 'element-plus'
import { ElMessage } from 'element-plus'
import platformJson from '@/json/platform.json'
import type {
Ilogistics,
IsortingInfo,
SortingPortAreaItem,
SortingPortCondition,
SortingPortOptionItem,
} from '@/types/api/logistics'
import {
createSortingApi,
updateSortingApi,
getSortingConfig,
getSortingPortAreaListApi,
} from '@/api/logistics'
const props = defineProps<{
logisticsMethodsList: Ilogistics[]
conditionTypeList: SortingPortOptionItem[]
operatorList: SortingPortOptionItem[]
}>()
const emit = defineEmits<{
success: []
}>()
const visible = ref(false)
const isEdit = ref(false)
const formRef = ref<FormInstance>()
const areaList = ref<SortingPortAreaItem[]>([])
const editId = ref<number>()
const createConditionItem = (): SortingPortCondition => ({
conditionType: '',
operator: '',
conditionValue: [],
})
const createDefaultForm = (): IsortingInfo => ({
sortingRemark: '',
sortingArea: null,
sortingAreaName: '',
matchType: 1,
sortNo: 1,
conditions: [createConditionItem()],
})
const form = ref<IsortingInfo>(createDefaultForm())
const getAvailableConditionTypes = (currentIndex: number) => {
const selectedFields = (form.value.conditions ?? [])
.filter((_, index) => index !== currentIndex)
.map((item) => item.conditionType)
return props.conditionTypeList?.filter(
(rule) => !selectedFields.includes(rule.code),
)
}
const handleConditionTypeChange = (item: SortingPortCondition) => {
item.conditionValue = []
item.operator = 'in'
}
const validateConditions = (
_rule: unknown,
_value: unknown,
callback: (error?: Error) => void,
) => {
const items = form.value.conditions ?? []
for (let i = 0; i < items.length; i++) {
const item = items[i]
const isValueValid =
Array.isArray(item.conditionValue) && item.conditionValue.length > 0
if (!item.conditionType || !isValueValid || !item.operator) {
callback(new Error('请完善分拣口规则'))
return
}
}
callback()
}
const rules = computed(() => ({
sortingRemark: [
{ required: true, message: '请输入分拣口说明', trigger: 'blur' },
],
sortingArea: [{ required: true, message: '请选择分拣口', trigger: 'change' }],
conditions: [
{ validator: validateConditions, trigger: 'blur' },
{ required: true, message: '请完善分拣口规则', trigger: 'change' },
],
sortNo: [{ required: true, message: '请输入序号', trigger: 'blur' }],
}))
const platformList = platformJson
const formatConditionsForForm = (conditions: SortingPortCondition[]) =>
conditions.map((item) => ({
...item,
conditionValue:
typeof item.conditionValue === 'string'
? item.conditionValue.split(',').filter(Boolean)
: Array.isArray(item.conditionValue)
? item.conditionValue
: [],
}))
const addConditionItem = () => {
form.value.conditions?.push(createConditionItem())
formRef.value?.validateField('conditions')
}
const removeConditionItem = (index: number) => {
form.value.conditions?.splice(index, 1)
formRef.value?.validateField('conditions')
}
const loadAreaList = async () => {
const res = await getSortingPortAreaListApi()
if (res.code !== 200) return
if (!isEdit.value) {
areaList.value = res.data.filter((item) => item.used === false)
} else {
areaList.value = res.data.filter(
(item) => item.used === false || item.code === form.value.sortingArea,
)
}
}
const handleSubmit = async () => {
await formRef.value?.validate()
const submitData: IsortingInfo = {
...form.value,
conditions: (form.value.conditions ?? []).map(
(item): SortingPortCondition => ({
...item,
conditionValue: Array.isArray(item.conditionValue)
? item.conditionValue.join(',')
: item.conditionValue,
}),
),
}
const res = isEdit.value
? await updateSortingApi({ ...submitData, id: editId.value })
: await createSortingApi(submitData)
if (res.code !== 200) return
ElMessage.success(isEdit.value ? '编辑成功' : '新增成功')
visible.value = false
emit('success')
}
const open = async (item?: IsortingInfo) => {
isEdit.value = !!item
editId.value = item?.id
form.value = createDefaultForm()
if (item?.id) {
const res = await getSortingConfig({ id: item.id })
if (res.code !== 200) return
const conditions = formatConditionsForForm(res.data.conditions ?? [])
form.value = {
...res.data,
conditions: conditions.length ? conditions : [createConditionItem()],
}
}
await loadAreaList()
visible.value = true
formRef.value?.clearValidate()
}
defineExpose({ open })
</script>
<style scoped lang="scss">
.item-info {
font-size: 12px;
color: #666;
line-height: 1;
margin-top: -15px;
}
.sorting-rules {
background-color: #eee;
border-radius: 3px;
padding: 5px 15px;
width: 100%;
.header {
font-size: 12px;
font-weight: 500;
display: flex;
align-items: center;
:deep(.el-radio__label) {
font-size: 12px;
}
}
.content {
display: flex;
align-items: center;
gap: 10px;
margin-top: 8px;
&:first-of-type {
margin-top: 6px;
}
}
.rule-action-btn {
font-size: 16px;
}
.platform-select {
:deep(.el-select__wrapper) {
height: var(--el-component-size);
min-height: var(--el-component-size);
box-sizing: border-box;
}
:deep(.el-select__selection) {
flex-wrap: nowrap;
}
:deep(.el-select__selected-item) {
flex-shrink: 1;
min-width: 0;
overflow: hidden;
}
:deep(.el-tag) {
max-width: 100%;
.el-tag__content {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
</style>
<style lang="scss">
.customize-select-style {
.el-select-dropdown__list {
width: 500px;
display: flex;
flex-wrap: wrap;
}
}
</style>
<template>
<div class="user-page flex-column card h-100 overflow-hidden">
<div class="flex-column h-100 overflow-hidden">
<div class="header-filter-form">
<el-form ref="searchFormRef" inline :model="searchForm">
<el-form-item label="分拣口">
<el-select
v-model="searchForm.sortingArea"
clearable
style="width: 160px"
placeholder="请选择分拣口"
>
<el-option
v-for="item in sortingList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="分拣口说明">
<el-input
v-model="searchForm.sortingRemark"
placeholder="请输入分拣口说明"
clearable
style="width: 220px"
/>
</el-form-item>
<el-form-item>
<el-button ref="searchBtnRef" type="primary" @click="search"
>查询</el-button
>
<el-button type="success" @click="createWarehouse">新增</el-button>
</el-form-item>
</el-form>
</div>
<div class="user-content flex-1 flex-column overflow-hidden">
<div class="user-list flex-1 overflow-hidden">
<div v-loading="loading" class="table-container">
<TableView :paginated-data="tableData" :columns="columns">
<template #conditions="{ row }">
<div class="conditions-text">
<div class="match-type">
{{
row.matchType === 1
? '满足以下所有条件:'
: '满足以下任一条件:'
}}
</div>
<div
v-for="(item, index) in row.conditions || []"
:key="index"
class="condition-item"
>
<span
class="condition-text"
:title="formatConditionText(item)"
>
{{ index + 1 }}{{ formatConditionText(item) }}
</span>
</div>
</div>
</template>
<template #operation="{ row }">
<el-button type="primary" link @click="updateSorting(row)">
编辑
</el-button>
<el-button type="danger" link @click="handleBatchDelete(row)">
删除
</el-button>
</template>
</TableView>
</div>
</div>
<ElPagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[100, 200, 300, 400, 500]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
style="margin: 10px auto 0; text-align: right"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></ElPagination>
</div>
</div>
<CreateSortingRuleDialog
ref="createDialogRef"
:logistics-methods-list="logisticsMethodsList"
:condition-type-list="conditionTypeList"
:operator-list="operatorList"
@success="search"
/>
</div>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { Ilogistics, IsortingInfo } from '@/types/api/logistics'
import { import {
Edit, Ilogistics,
Delete, IsortingInfo,
// CirclePlus, SortingPortCondition,
QuestionFilled, SortingPortOptionItem,
} from '@element-plus/icons-vue' } from '@/types/api/logistics'
import platformJson from '@/json/platform.json'
// import useImagePreview from '@/utils/hooks/useImagePreview.'
// const { mouseoverImg, mouseleaveImg } = useImagePreview()
import sortingImg from '@/assets/images/分拣口说明.png'
import { import {
getsortingConfigListApi, getsortingConfigListApi,
// usableAllList,
createSortingApi,
updateSortingApi,
deleteSortingApi, deleteSortingApi,
getSortingConfig, getSortingPortAreaListApi,
getSortPortConditionTypeListApi,
getSortPortOperatorListApi,
} from '@/api/logistics' } from '@/api/logistics'
import { allErpCodeListApi } from '@/api/podCnOrder' import { allErpCodeListApi } from '@/api/podCnOrder'
import { nextTick, ref } from 'vue' import { onMounted, ref } from 'vue'
import CreateSortingRuleDialog from './components/CreateSortingRuleDialog.vue'
import { useEnterKeyTrigger } from '@/utils/hooks/useEnterKeyTrigger.ts' import { useEnterKeyTrigger } from '@/utils/hooks/useEnterKeyTrigger.ts'
import TableView from '@/components/TableView.vue'
import type { CustomColumn } from '@/types/table'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
interface InterForm {
id?: number
sortingFacility?: string | null
sortingArea: number | null
sortingAreaName?: string | null
serviceCode: string[]
}
const searchForm = ref({
sortingFacility: '',
sortingArea: null,
serviceCode: [],
})
const selections = ref<IsortingInfo[]>([])
const formRef = ref()
const form = ref<InterForm>({
sortingFacility: '',
sortingArea: null,
sortingAreaName: '',
serviceCode: [],
})
const createData = ref({
title: '',
show: false,
isEdit: false,
form: {
sortingFacility: '',
sortingArea: null,
sortingAreaName: '',
serviceCode: [],
},
})
const activeName = ref('1')
const rules = computed(() => { const columns: CustomColumn<IsortingInfo>[] = [
return {
sortingFacility: [
{ required: true, message: '请输入分拣设备', trigger: 'blur' },
],
sortingArea: [
{ required: true, message: '请选择分拣口', trigger: 'change' },
],
serviceCode: [
{ {
required: true, key: 'sortNo',
message: `请选择${activeName.value == '1' ? '物流公司' : '平台'}`, prop: 'sortNo',
trigger: 'change', label: '序号',
width: 100,
align: 'center',
}, },
], {
} key: 'conditions',
prop: 'conditions',
slot: 'conditions',
label: '分拣口规则',
minWidth: 250,
align: 'left',
showOverflowTooltip: false,
},
{
key: 'sortingAreaName',
prop: 'sortingAreaName',
label: '分拣口',
width: 100,
align: 'center',
},
{
key: 'sortingRemark',
prop: 'sortingRemark',
label: '分拣口说明',
minWidth: 200,
align: 'left',
},
{
key: 'operation',
label: '操作',
width: 130,
align: 'center',
slot: 'operation',
},
]
const searchForm = ref({
sortingArea: null,
sortingRemark: '',
}) })
const createDialogRef = ref<InstanceType<typeof CreateSortingRuleDialog>>()
const { const {
loading,
currentPage, currentPage,
pageSize, pageSize,
total, total,
...@@ -87,8 +177,6 @@ const { ...@@ -87,8 +177,6 @@ const {
getsortingConfigListApi( getsortingConfigListApi(
{ {
...searchForm.value, ...searchForm.value,
serviceCode: searchForm.value.serviceCode.join(','),
type: activeName.value,
}, },
page, page,
pageSize, pageSize,
...@@ -107,96 +195,67 @@ const getlogisticsMethods = async () => { ...@@ -107,96 +195,67 @@ const getlogisticsMethods = async () => {
] ]
} }
interface interSorting { interface SortingOption {
label: string label: string
value: number value: number
} }
const sortingList = ref<interSorting[]>([ const sortingList = ref<SortingOption[]>([])
{ const getSortingList = async () => {
label: 'L1', const res = await getSortingPortAreaListApi()
value: 1, sortingList.value = (res.data || []).map((item) => ({
}, label: item.desc,
{ value: item.code,
label: 'R1', }))
value: 2,
},
{
label: 'L2',
value: 3,
},
{
label: 'R2',
value: 4,
},
{
label: 'L3',
value: 5,
},
{
label: 'R3',
value: 6,
},
{
label: 'L4',
value: 7,
},
{
label: 'R4',
value: 8,
},
{
label: 'L5',
value: 9,
},
{
label: 'R5',
value: 10,
},
{
label: 'L6',
value: 11,
},
{
label: 'R6',
value: 12,
},
{
label: 'L7',
value: 13,
},
{
label: 'R7',
value: 14,
},
{
label: 'E0',
value: 99,
},
])
const handleSelectionChange = (data: IsortingInfo[]) => {
selections.value = data
} }
const handleConfirm = async () => {
await formRef.value?.validate() const conditionTypeList = ref<SortingPortOptionItem[]>([])
if (!createData.value.isEdit) { const operatorList = ref<SortingPortOptionItem[]>([])
await createSortingApi({
...form.value, const loadConditionTypeList = async () => {
serviceCode: form.value.serviceCode.join(','), const res = await getSortPortConditionTypeListApi()
type: activeName.value, if (res.code !== 200) return
}) conditionTypeList.value = res.data
} else { }
await updateSortingApi({
...form.value, const loadOperatorList = async () => {
serviceCode: form.value.serviceCode.join(','), const res = await getSortPortOperatorListApi()
type: activeName.value, if (res.code !== 200) return
}) operatorList.value = res.data
}
const formatConditionText = (item: SortingPortCondition) => {
const typeDesc =
conditionTypeList.value.find((r) => r.code === item.conditionType)?.desc ??
item.conditionType
const operatorDesc =
operatorList.value.find((r) => r.code === item.operator)?.desc ??
item.operator
let valueText = ''
switch (item.conditionType) {
case 'order_platform':
valueText = String(item.conditionValue ?? '')
.split(',')
.filter(Boolean)
.join('、')
break
default:
valueText = String(item.conditionValue ?? '')
.split(',')
.filter(Boolean)
.map(
(code) =>
logisticsMethodsList.value.find((l) => l.code === code)
?.basicsName ?? code,
)
.join('、')
break
} }
createData.value.show = false return `${typeDesc}${operatorDesc} ${valueText}`
ElMessage.success('操作成功')
await search()
} }
const handleBatchDelete = async (row: IsortingInfo | null) => { const handleBatchDelete = async (row: IsortingInfo | null) => {
if (!row && !selections.value.length) { if (!row?.id) {
return ElMessage.warning('请选择要删除的数据') return ElMessage.warning('请选择要删除的数据')
} }
await ElMessageBox.confirm('确定要删除吗?', '提示', { await ElMessageBox.confirm('确定要删除吗?', '提示', {
...@@ -204,503 +263,83 @@ const handleBatchDelete = async (row: IsortingInfo | null) => { ...@@ -204,503 +263,83 @@ const handleBatchDelete = async (row: IsortingInfo | null) => {
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning', type: 'warning',
}) })
const str = await deleteSortingApi({ id: row.id })
row && row.id
? row.id?.toString()
: selections.value.map((el: IsortingInfo) => el.id).join(',')
await deleteSortingApi({ ids: str, type: activeName.value })
ElMessage.success('删除成功') ElMessage.success('删除成功')
search() search()
} }
// const addLogistics = () => {
// ElMessageBox.prompt('', '物流方式', {
// confirmButtonText: '确定',
// cancelButtonText: '取消',
// inputErrorMessage: '请输入物流方式',
// inputPlaceholder: '请输入物流方式',
// }).then(({ value }: { value: string }) => {
// if (!value.trim()) {
// ElMessage.error('物流方式不能为空')
// return
// }
// if (form.value.serviceCode.includes(value)) {
// ElMessage.warning('物流方式已存在')
// return
// }
// form.value.serviceCode.push(value)
// ElMessage.success('添加成功')
// })
// }
const createWarehouse = () => { const createWarehouse = () => {
createData.value.show = true createDialogRef.value?.open()
createData.value.isEdit = false
createData.value.title = '新增'
form.value = {
sortingFacility: '',
sortingArea: null,
sortingAreaName: '',
serviceCode: [],
}
nextTick(() => {
formRef.value?.clearValidate()
})
} }
const updateSorting = async (item: IsortingInfo) => { const updateSorting = (item: IsortingInfo) => {
if (!item) return if (!item) return
createData.value.show = true createDialogRef.value?.open(item)
createData.value.isEdit = true
createData.value.title = '修改'
const res = await getSortingConfig({ id: item.id as number | string })
if (res.code !== 200) return
const { serviceCode, ...rest } = res.data
form.value = {
...rest,
serviceCode: serviceCode ? serviceCode.split(',') : [],
}
nextTick(() => {
formRef.value?.clearValidate()
})
}
getlogisticsMethods()
const sortingChange = (v: number) => {
const warehouse = sortingList.value.find((w: interSorting) => w.value == v)
form.value.sortingAreaName = warehouse ? warehouse.label : ''
} }
const handleClick = () => { onMounted(async () => {
const loading = ElLoading.service({ await Promise.all([
fullscreen: true, getSortingList(),
text: '操作中...', getlogisticsMethods(),
background: 'rgba(0, 0, 0, 0.3)', loadConditionTypeList(),
}) loadOperatorList(),
try { ])
searchForm.value = { })
sortingFacility: '',
sortingArea: null,
serviceCode: [],
}
search()
} catch (error) {
console.log(error)
} finally {
loading.close()
}
}
const searchFormRef = ref() const searchFormRef = ref()
const searchBtnRef = ref() const searchBtnRef = ref()
useEnterKeyTrigger({ useEnterKeyTrigger({
formRef: searchFormRef, formRef: searchFormRef,
btnRef: searchBtnRef, btnRef: searchBtnRef,
callback: () => {
callback: (event: KeyboardEvent) => {
console.log('回车键被按下', event)
search() search()
}, },
}) })
</script> </script>
<template>
<div class="user-page flex-column card h-100 overflow-hidden">
<el-tabs
v-model="activeName"
type="card"
class="demo-tabs"
@tab-change="handleClick"
>
<el-tab-pane
label="按物流方式分拣(优先级高)"
name="1"
class="flex-column h-100 overflow-hidden"
>
<div class="header-filter-form">
<el-form inline :model="searchForm" ref="searchFormRef">
<el-form-item label="分拣设备">
<el-input
v-model.trim="searchForm.sortingFacility"
placeholder="分拣设备"
clearable
></el-input>
</el-form-item>
<el-form-item label="分拣口">
<el-select
v-model="searchForm.sortingArea"
clearable
style="width: 160px"
>
<el-option
v-for="item in sortingList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="物流公司">
<el-select
v-model="searchForm.serviceCode"
clearable
filterable
multiple
collapse-tags
collapse-tags-tooltip
style="width: 220px"
>
<el-option
v-for="item in logisticsMethodsList"
:key="item.code"
:label="item.basicsName"
:value="item.code"
></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="search" ref="searchBtnRef"
>查询</el-button
>
<el-button type="success" @click="createWarehouse"
>新增</el-button
>
<el-button type="danger" @click="handleBatchDelete(null)"
>删除</el-button
>
</el-form-item>
</el-form>
</div>
<div class="user-content flex-1 flex-column overflow-hidden">
<div class="user-list flex-1 overflow-hidden">
<el-table
height="100%"
:data="tableData"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" />
<el-table-column
type="index"
label="序号"
width="60"
align="center"
/>
<el-table-column
align="center"
label="分拣设备"
show-overflow-tooltip
prop="sortingFacility"
></el-table-column>
<el-table-column
align="center"
label="分拣口名称"
prop="sortingAreaName"
>
</el-table-column>
<el-table-column
align="center"
label="物流公司"
show-overflow-tooltip
prop="serviceCode"
></el-table-column>
<el-table-column label="操作" align="center" width="100">
<template #default="{ row }">
<el-icon
size="24"
title="编辑"
color="#EF6C00"
style="cursor: pointer; vertical-align: middle"
@click="updateSorting(row)"
>
<Edit />
</el-icon>
<el-icon
size="24"
title="删除"
color="#f56c6c"
style="cursor: pointer; vertical-align: middle"
@click="handleBatchDelete(row)"
>
<Delete />
</el-icon>
</template>
</el-table-column>
</el-table>
</div>
<ElPagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[100, 200, 300, 400, 500]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
style="margin: 10px auto 0; text-align: right"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></ElPagination>
</div>
</el-tab-pane>
<el-tab-pane
label="按平台分拣(优先级低)"
name="2"
class="flex-column h-100 overflow-hidden"
>
<div class="header-filter-form">
<el-form inline :model="searchForm">
<el-form-item label="分拣设备">
<el-input
v-model.trim="searchForm.sortingFacility"
placeholder="分拣设备"
clearable
></el-input>
</el-form-item>
<el-form-item label="分拣口">
<el-select
v-model="searchForm.sortingArea"
clearable
style="width: 160px"
>
<el-option
v-for="item in sortingList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="平台">
<el-select
v-model="searchForm.serviceCode"
clearable
filterable
multiple
collapse-tags
collapse-tags-tooltip
popper-class="customize-select-style"
style="width: 200px"
>
<el-option
v-for="item in platformJson"
:key="item.type"
:label="item.type"
:value="item.type"
>
<img
:src="`/images/icon/${item.icon.split('/').pop()}`"
style="height: 20px; margin: 5px 10px 0 0"
/>
<span :title="item.type">{{ item.type }}</span></el-option
>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="search">查询</el-button>
<el-button type="success" @click="createWarehouse"
>新增</el-button
>
<el-button type="danger" @click="handleBatchDelete(null)"
>删除</el-button
>
</el-form-item>
</el-form>
</div>
<div class="user-content flex-1 flex-column overflow-hidden">
<div class="user-list flex-1 overflow-hidden">
<el-table
height="100%"
:data="tableData"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" />
<el-table-column
type="index"
label="序号"
width="60"
align="center"
/>
<el-table-column
align="center"
label="分拣设备"
show-overflow-tooltip
prop="sortingFacility"
></el-table-column>
<el-table-column
align="center"
label="分拣口名称"
prop="sortingAreaName"
>
</el-table-column>
<el-table-column
align="center"
label="平台"
show-overflow-tooltip
prop="serviceCode"
></el-table-column>
<el-table-column label="操作" align="center" width="100">
<template #default="{ row }">
<el-icon
size="24"
title="编辑"
color="#EF6C00"
style="cursor: pointer; vertical-align: middle"
@click="updateSorting(row)"
>
<Edit />
</el-icon>
<el-icon
size="24"
title="删除"
color="#f56c6c"
style="cursor: pointer; vertical-align: middle"
@click="handleBatchDelete(row)"
>
<Delete />
</el-icon>
</template>
</el-table-column>
</el-table>
</div>
<ElPagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[100, 200, 300, 400, 500]"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
style="margin: 10px auto 0; text-align: right"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
></ElPagination></div
></el-tab-pane>
</el-tabs>
<el-dialog
v-model="createData.show"
width="500px"
:title="createData.title"
>
<el-form ref="formRef" label-width="110px" :rules="rules" :model="form">
<el-form-item label="分拣设备" prop="sortingFacility">
<el-input
v-model.trim="form.sortingFacility"
placeholder="分拣设备"
clearable
></el-input>
</el-form-item>
<el-form-item label="分拣口" prop="sortingArea">
<div class="flex">
<el-select
v-model="form.sortingArea"
clearable
style="width: 320px"
@change="sortingChange"
>
<el-option
v-for="item in sortingList"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
&ensp;
<el-popover placement="bottom-start" width="700" trigger="click">
<img :src="sortingImg" alt="分拣口示意图" style="width: 100%" />
<template #reference>
<el-icon size="30" color="#606266" style="cursor: pointer">
<QuestionFilled />
</el-icon>
</template>
</el-popover>
<!-- <el-icon
size="30"
title="分拣口示意图"
style="cursor: pointer"
@click="mouseoverImg(ev, sortingImg, '700px', true, true)"
@mouseover="(ev:MouseEvent) => mouseoverImg(ev, sortingImg,'700px',true,true)"
@mouseleave="mouseleaveImg"
><QuestionFilled
/></el-icon> -->
</div>
</el-form-item>
<el-form-item
v-if="activeName == '1'"
label="物流公司"
prop="serviceCode"
>
<div class="flex">
<el-select
v-model="form.serviceCode"
style="width: 320px"
clearable
filterable
multiple
>
<el-option
v-for="item in logisticsMethodsList"
:key="item.code"
:label="item.basicsName"
:value="item.code"
></el-option>
</el-select>
<!-- &ensp;
<el-icon
color="#409eff"
size="30"
title="添加物流方式"
class="add-btn"
@click="addLogistics"
><CirclePlus
/></el-icon> -->
</div>
</el-form-item>
<el-form-item label="平台" v-else prop="serviceCode">
<el-select
v-model="form.serviceCode"
clearable
filterable
multiple
popper-class="customize-select-style"
>
<el-option
v-for="item in platformJson"
:key="item.type"
:label="item.type"
:value="item.type"
>
<img
:src="`/images/icon/${item.icon.split('/').pop()}`"
style="height: 20px; margin: 5px 10px 0 0"
/>
<span :title="item.type">{{ item.type }}</span></el-option
>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="createData.show = false">取消</el-button>
<el-button type="primary" @click="handleConfirm">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<style scoped lang="scss"> <style scoped lang="scss">
.user-list {
.table-container {
height: 100%;
overflow: hidden;
}
}
.el-card { .el-card {
::v-deep(.el-card__body) { ::v-deep(.el-card__body) {
height: 100%; height: 100%;
} }
} }
.customize-select-style { .conditions-text {
.el-select-dropdown__list { padding: 4px 0;
width: 500px; line-height: 1.6;
overflow: hidden;
.match-type {
color: #666;
margin-bottom: 2px;
}
.condition-item {
display: flex; display: flex;
flex-wrap: wrap; align-items: center;
.el-select-dropdown__item { color: #333;
width: 160px; overflow: hidden;
min-width: 0;
.condition-text {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.category-detail-btn {
flex-shrink: 0;
margin-left: 4px;
padding: 0;
height: auto;
} }
} }
} }
.manage { .manage {
height: 100%; height: 100%;
display: flex; display: flex;
...@@ -742,10 +381,4 @@ useEnterKeyTrigger({ ...@@ -742,10 +381,4 @@ useEnterKeyTrigger({
} }
} }
} }
.add-btn :hover {
cursor: pointer;
}
.demo-tabs {
height: 100%;
}
</style> </style>
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
:title="isEdit ? '编辑配货区' : '新增配货区'" :title="isEdit ? '编辑配货区' : '新增配货区'"
:close-on-click-modal="false" :close-on-click-modal="false"
width="800px" width="800px"
destroy-on-close
> >
<el-form ref="formRef" label-width="170px" :model="form" :rules="rules"> <el-form ref="formRef" label-width="170px" :model="form" :rules="rules">
<el-form-item label="配货区说明" prop="areaDesc"> <el-form-item label="配货区说明" prop="areaDesc">
...@@ -63,9 +64,9 @@ ...@@ -63,9 +64,9 @@
<el-select <el-select
v-model="item.operator" v-model="item.operator"
style="width: 160px" style="width: 100px"
:disabled="isDisabled" :disabled="isDisabled"
placeholder="请选择关系" placeholder="关系"
> >
<el-option <el-option
v-for="operator in getAvailableOperators(item.conditionType)" v-for="operator in getAvailableOperators(item.conditionType)"
...@@ -79,13 +80,12 @@ ...@@ -79,13 +80,12 @@
<el-select <el-select
v-model="item.conditionValue" v-model="item.conditionValue"
class="platform-select" class="platform-select"
style="width: 160px" style="width: 190px"
:disabled="isDisabled" :disabled="isDisabled"
placeholder="请选择平台" placeholder="请选择平台"
filterable filterable
multiple multiple
collapse-tags collapse-tags
collapse-tags-tooltip
:max-collapse-tags="1" :max-collapse-tags="1"
clearable clearable
popper-class="customize-select-style" popper-class="customize-select-style"
...@@ -105,11 +105,47 @@ ...@@ -105,11 +105,47 @@
</el-option> </el-option>
</el-select> </el-select>
</template> </template>
<template v-else-if="item.conditionType === 'order_category'">
<el-cascader
v-model="item.conditionValue"
class="category-cascader"
style="width: 190px"
placeholder="请选择商品类目"
:options="categoryList"
:props="{
label: 'name',
value: 'id',
children: 'children',
multiple: true,
emitPath: true,
}"
collapse-tags
collapse-tags-tooltip
:max-collapse-tags="1"
tag-type="info"
show-checked-strategy="parent"
clearable
:disabled="isDisabled"
>
<template #tag="{ data, deleteTag }">
<el-tag
v-for="tag in data.slice(0, 1)"
:key="tag.key"
type="info"
:closable="tag.closable"
class="category-cascader-tag"
@close="deleteTag(tag)"
>
{{ tag.text }}
</el-tag>
</template>
</el-cascader>
</template>
<template v-else> <template v-else>
<el-input-number <el-input-number
v-model="item.conditionValue" v-model="item.conditionValue"
style="width: 160px" style="width: 190px"
:min="1" :min="1"
:max="999" :max="999"
:disabled="isDisabled" :disabled="isDisabled"
...@@ -202,19 +238,7 @@ import type { ...@@ -202,19 +238,7 @@ import type {
SortingRuleCondition, SortingRuleCondition,
SortingRuleDetail, SortingRuleDetail,
} from '@/types/api/order' } from '@/types/api/order'
import { import { getAreaListApi, addAreaApi, editAreaApi } from '@/api/order'
getAreaListApi,
getConditionTypeListApi,
getOperatorListApi,
addAreaApi,
editAreaApi,
} from '@/api/order'
// 配货规则条件
const pickingRuleList = ref<OperatorList[]>()
// 配货规则操作符
const operatorList = ref<OperatorList[]>()
const props = defineProps<{ const props = defineProps<{
modelValue: boolean modelValue: boolean
...@@ -223,6 +247,9 @@ const props = defineProps<{ ...@@ -223,6 +247,9 @@ const props = defineProps<{
warehouseId: number warehouseId: number
warehouseName: string warehouseName: string
isDisabled?: boolean isDisabled?: boolean
categoryList: OperatorList[]
pickingRuleList: OperatorList[]
operatorList: OperatorList[]
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void (e: 'update:modelValue', value: boolean): void
...@@ -264,33 +291,38 @@ const getAvailablePickingRules = (currentIndex: number) => { ...@@ -264,33 +291,38 @@ const getAvailablePickingRules = (currentIndex: number) => {
const selectedFields = form.value.conditions const selectedFields = form.value.conditions
.filter((_, index) => index !== currentIndex) .filter((_, index) => index !== currentIndex)
.map((item) => item.conditionType) .map((item) => item.conditionType)
return pickingRuleList.value?.filter( return props.pickingRuleList.filter(
(rule) => !selectedFields.includes(rule.code), (rule) => !selectedFields.includes(rule.code),
) )
} }
// 根据配货规则类型获取可用的操作符 // 根据配货规则类型获取可用的操作符
const getAvailableOperators = (conditionType: string) => { const getAvailableOperators = (conditionType: string) => {
const list = operatorList.value ?? [] const list = props.operatorList
if (conditionType === 'order_total_num') { const matchers: Record<string, (code: string) => boolean> = {
return list.filter((operator) => operator.code !== 'in') order_total_num: (code) => code !== 'in' && code !== 'contain',
} else if (conditionType === 'order_platform') { order_platform: (code) => code === 'in',
return list.filter((operator) => operator.code === 'in') order_category: (code) => code === 'contain',
} }
return list const matcher = matchers[conditionType]
return matcher ? list.filter((operator) => matcher(operator.code)) : list
} }
// 配货规则条件改变 // 配货规则条件改变
const handleConditionTypeChange = (item: SortingRuleCondition) => { const handleConditionTypeChange = (item: SortingRuleCondition) => {
// 平台 只能选 in属于 switch (item.conditionType) {
if (item.conditionType === 'order_platform') { case 'order_platform':
item.conditionValue = undefined item.conditionValue = undefined
item.operator = 'in' item.operator = 'in'
} break
// 订单总件数 清空字段 case 'order_total_num':
else if (item.conditionType === 'order_total_num') {
item.conditionValue = undefined item.conditionValue = undefined
item.operator = 'gt' item.operator = 'gt'
break
case 'order_category':
item.conditionValue = undefined
item.operator = 'contain'
break
} }
} }
...@@ -311,7 +343,8 @@ const validateConditions = ( ...@@ -311,7 +343,8 @@ const validateConditions = (
for (let i = 0; i < items.length; i++) { for (let i = 0; i < items.length; i++) {
const item = items[i] const item = items[i]
const isValueValid = const isValueValid =
item.conditionType === 'order_platform' item.conditionType === 'order_platform' ||
item.conditionType === 'order_category'
? Array.isArray(item.conditionValue) && item.conditionValue.length > 0 ? Array.isArray(item.conditionValue) && item.conditionValue.length > 0
: item.conditionValue !== null && item.conditionValue !== undefined : item.conditionValue !== null && item.conditionValue !== undefined
if (!item.conditionType || !isValueValid || !item.operator) { if (!item.conditionType || !isValueValid || !item.operator) {
...@@ -352,14 +385,29 @@ const rules = computed(() => { ...@@ -352,14 +385,29 @@ const rules = computed(() => {
const platformList = platformJson const platformList = platformJson
// 将详情返回的字符串条件值转为表单可用格式
const formatConditionsForForm = (conditions: SortingRuleCondition[]) => const formatConditionsForForm = (conditions: SortingRuleCondition[]) =>
conditions.map((item) => ({ conditions.map((item) => {
if (
typeof item.conditionValue !== 'string' ||
(item.conditionType !== 'order_platform' &&
item.conditionType !== 'order_category')
) {
return item
}
const values = item.conditionValue.split(',').filter(Boolean)
// 类目 id 一般为 number,需与级联 options 的 id 类型一致才能回显
if (item.conditionType === 'order_category') {
return {
...item, ...item,
conditionValue: conditionValue: values.map((v) => {
typeof item.conditionValue === 'string' const num = Number(v)
? item.conditionValue.split(',').filter(Boolean) return Number.isNaN(num) ? v : num
: item.conditionValue, }),
})) }
}
return { ...item, conditionValue: values }
})
// 添加配货规则 // 添加配货规则
const addPickingRuleItem = () => { const addPickingRuleItem = () => {
...@@ -397,20 +445,6 @@ const loadAreaList = async () => { ...@@ -397,20 +445,6 @@ const loadAreaList = async () => {
} }
} }
// 获取配货规则条件
const getConditionTypeList = async () => {
const res = await getConditionTypeListApi()
if (res.code !== 200) return
pickingRuleList.value = res.data
}
// 获取配货规则操作符
const getOperatorList = async () => {
const res = await getOperatorListApi()
if (res.code !== 200) return
operatorList.value = res.data
}
// 提交 // 提交
const handleSubmit = async () => { const handleSubmit = async () => {
form.value.warehouseId = props.warehouseId form.value.warehouseId = props.warehouseId
...@@ -425,7 +459,9 @@ const handleSubmit = async () => { ...@@ -425,7 +459,9 @@ const handleSubmit = async () => {
(item): SortingRuleCondition => ({ (item): SortingRuleCondition => ({
...item, ...item,
conditionValue: Array.isArray(item.conditionValue) conditionValue: Array.isArray(item.conditionValue)
? item.conditionValue.join(',') ? item.conditionValue
.map((v) => (Array.isArray(v) ? v[v.length - 1] : v))
.join(',')
: item.conditionValue, : item.conditionValue,
}), }),
), ),
...@@ -442,7 +478,7 @@ const handleSubmit = async () => { ...@@ -442,7 +478,7 @@ const handleSubmit = async () => {
} }
onMounted(async () => { onMounted(async () => {
await Promise.all([loadAreaList(), getConditionTypeList(), getOperatorList()]) await loadAreaList()
if (props.editData) { if (props.editData) {
form.value = { form.value = {
...props.editData, ...props.editData,
...@@ -500,7 +536,6 @@ onMounted(async () => { ...@@ -500,7 +536,6 @@ onMounted(async () => {
:deep(.el-select__selection) { :deep(.el-select__selection) {
flex-wrap: nowrap; flex-wrap: nowrap;
overflow: hidden;
} }
:deep(.el-select__selected-item) { :deep(.el-select__selected-item) {
...@@ -529,4 +564,21 @@ onMounted(async () => { ...@@ -529,4 +564,21 @@ onMounted(async () => {
flex-wrap: wrap; flex-wrap: wrap;
} }
} }
// cascader 样式,确保单行显示
.category-cascader {
.el-cascader__tags {
display: flex !important;
flex-wrap: nowrap !important;
align-items: center;
overflow: hidden;
}
.category-cascader-tag {
flex: 0 1 auto !important;
min-width: 0 !important;
max-width: 100%;
overflow: hidden;
}
}
</style> </style>
...@@ -47,6 +47,62 @@ ...@@ -47,6 +47,62 @@
class="table-container" class="table-container"
> >
<TableView :paginated-data="sortingList" :columns="columns"> <TableView :paginated-data="sortingList" :columns="columns">
<template #conditions="{ row }">
<div v-if="row.areaCode === 'AREA_0'">默认分拣口</div>
<div v-else class="conditions-text">
<div class="match-type">
{{
row.matchType === 1
? '满足以下所有条件:'
: '满足以下任一条件:'
}}
</div>
<div
v-for="(item, index) in row.conditions || []"
:key="index"
class="condition-item"
>
<span
class="condition-text"
:title="formatConditionText(item)"
>
{{ index + 1 }}{{ formatConditionText(item) }}
</span>
<el-popover
v-if="item.conditionType === 'order_category'"
placement="bottom"
:width="'auto'"
trigger="click"
>
<el-cascader-panel
class="category-cascader-panel-readonly"
:model-value="getCategoryCascaderValue(item)"
:options="categoryList"
:props="{
label: 'name',
value: 'id',
children: 'children',
multiple: true,
emitPath: true,
checkOnClickNode: false,
checkOnClickLeaf: false,
showPrefix: false,
}"
/>
<template #reference>
<el-button
type="primary"
link
class="category-detail-btn"
>
详情
</el-button>
</template>
</el-popover>
</div>
</div>
</template>
<template #autoPrint="{ row }"> <template #autoPrint="{ row }">
{{ row.autoPrint ? '开启' : '关闭' }} {{ row.autoPrint ? '开启' : '关闭' }}
</template> </template>
...@@ -78,6 +134,9 @@ ...@@ -78,6 +134,9 @@
:edit-data="editData" :edit-data="editData"
:warehouse-id="searchForm.warehouseId" :warehouse-id="searchForm.warehouseId"
:warehouse-name="searchForm.warehouseName" :warehouse-name="searchForm.warehouseName"
:category-list="categoryList"
:picking-rule-list="pickingRuleList"
:operator-list="operatorList"
@submit="getSortingRuleList" @submit="getSortingRuleList"
/> />
<el-dialog <el-dialog
...@@ -97,7 +156,9 @@ import { loadWarehouseListApi } from '@/api/common' ...@@ -97,7 +156,9 @@ import { loadWarehouseListApi } from '@/api/common'
import type { WarehouseListData } from '@/types' import type { WarehouseListData } from '@/types'
import type { import type {
LogListData, LogListData,
OperatorList,
SortingList, SortingList,
SortingRuleCondition,
SortingRuleDetail, SortingRuleDetail,
} from '@/types/api/order' } from '@/types/api/order'
import type { CustomColumn } from '@/types/table' import type { CustomColumn } from '@/types/table'
...@@ -107,6 +168,9 @@ import { ...@@ -107,6 +168,9 @@ import {
getConfigApi, getConfigApi,
updateConfighApi, updateConfighApi,
getAreaInfoApi, getAreaInfoApi,
getCategoryListApi,
getConditionTypeListApi,
getOperatorListApi,
} from '@/api/order' } from '@/api/order'
import TableView from '@/components/TableView.vue' import TableView from '@/components/TableView.vue'
import LogList from '@/components/LogList.vue' import LogList from '@/components/LogList.vue'
...@@ -128,11 +192,13 @@ const columns: CustomColumn<SortingList>[] = [ ...@@ -128,11 +192,13 @@ const columns: CustomColumn<SortingList>[] = [
align: 'center', align: 'center',
}, },
{ {
key: 'conditionsText', key: 'conditions',
prop: 'conditionsText', prop: 'conditions',
label: '配货规则', label: '配货规则',
minWidth: 250, minWidth: 250,
align: 'left', align: 'left',
slot: 'conditions',
showOverflowTooltip: false,
}, },
{ {
key: 'areaName', key: 'areaName',
...@@ -174,6 +240,9 @@ const columns: CustomColumn<SortingList>[] = [ ...@@ -174,6 +240,9 @@ const columns: CustomColumn<SortingList>[] = [
const warehouseList = ref<WarehouseListData[]>([]) // 仓库列表 const warehouseList = ref<WarehouseListData[]>([]) // 仓库列表
const sortingList = ref<SortingList[]>([]) // 规则列表 const sortingList = ref<SortingList[]>([]) // 规则列表
const categoryList = ref<OperatorList[]>([]) // 商品类目
const pickingRuleList = ref<OperatorList[]>([]) // 配货规则条件
const operatorList = ref<OperatorList[]>([]) // 配货规则操作符
const isEnableSorting = ref(false) // 是否开启配货分拣 const isEnableSorting = ref(false) // 是否开启配货分拣
const currentId = ref(0) // 当前操作id const currentId = ref(0) // 当前操作id
const isEdit = ref(false) // 是否编辑 const isEdit = ref(false) // 是否编辑
...@@ -188,6 +257,139 @@ const searchForm = ref({ ...@@ -188,6 +257,139 @@ const searchForm = ref({
warehouseName: '', warehouseName: '',
}) })
type CategoryNode = OperatorList & {
id?: number | string
name?: string
children?: CategoryNode[]
}
// 获取节点下全部叶子 id
const getCategoryLeafIds = (node: CategoryNode): string[] => {
if (!node.children?.length) return node.id != null ? [String(node.id)] : []
return node.children.flatMap(getCategoryLeafIds)
}
// 格式化已选类目:某项下所有子项都被选中时,只展示该项路径
const formatSelectedCategories = (
list: CategoryNode[],
selectedIds: Set<string>,
path: string[] = [],
): string[] => {
const result: string[] = []
for (const item of list) {
const currentPath = [...path, item.name ?? '']
const leafIds = getCategoryLeafIds(item)
const allSelected =
leafIds.length > 0 && leafIds.every((id) => selectedIds.has(id))
if (allSelected) {
result.push(currentPath.join('->'))
continue
}
if (item.children?.length) {
result.push(
...formatSelectedCategories(item.children, selectedIds, currentPath),
)
} else if (item.id != null && selectedIds.has(String(item.id))) {
result.push(currentPath.join('->'))
}
}
return result
}
// 根据叶子 id 查找类目完整路径(用于级联回显)
const findCategoryPath = (
list: CategoryNode[],
targetId: string | number,
path: (string | number)[] = [],
): (string | number)[] | null => {
for (const item of list) {
if (item.id == null) continue
const currentPath = [...path, item.id]
if (String(item.id) === String(targetId)) return currentPath
if (item.children?.length) {
const found = findCategoryPath(item.children, targetId, currentPath)
if (found) return found
}
}
return null
}
// 将条件值转为级联回显路径
const getCategoryCascaderValue = (item: SortingRuleCondition) => {
const ids = String(item.conditionValue ?? '')
.split(',')
.filter(Boolean)
return ids
.map((id) => {
const num = Number(id)
return findCategoryPath(
categoryList.value as CategoryNode[],
Number.isNaN(num) ? id : num,
)
})
.filter((path): path is (string | number)[] => !!path)
}
// 配货规则展示文案
const formatConditionText = (item: SortingRuleCondition) => {
// 配货规则条件
const typeDesc =
pickingRuleList.value.find((r) => r.code === item.conditionType)?.desc ??
item.conditionType
// 配货规则操作符
const operatorDesc =
operatorList.value.find((r) => r.code === item.operator)?.desc ??
item.operator
let valueText = ''
switch (item.conditionType) {
case 'order_category': {
const selectedIds = new Set(
String(item.conditionValue ?? '')
.split(',')
.filter(Boolean),
)
valueText = formatSelectedCategories(
categoryList.value as CategoryNode[],
selectedIds,
).join('、')
break
}
case 'order_platform':
valueText = String(item.conditionValue ?? '')
.split(',')
.filter(Boolean)
.join('、')
break
default:
valueText = String(item.conditionValue ?? '')
break
}
return `${typeDesc}${operatorDesc} ${valueText}`
}
// 获取商品类目
const getCategoryList = async () => {
const res = await getCategoryListApi()
if (res.code !== 200) return
categoryList.value = res.data
}
// 获取配货规则条件
const getConditionTypeList = async () => {
const res = await getConditionTypeListApi()
if (res.code !== 200) return
pickingRuleList.value = res.data
}
// 获取配货规则操作符
const getOperatorList = async () => {
const res = await getOperatorListApi()
if (res.code !== 200) return
operatorList.value = res.data
}
// 加载仓库列表 // 加载仓库列表
const loadWarehouseList = async () => { const loadWarehouseList = async () => {
try { try {
...@@ -303,9 +505,16 @@ const deleteArea = async (id: number) => { ...@@ -303,9 +505,16 @@ const deleteArea = async (id: number) => {
// } // }
onMounted(async () => { onMounted(async () => {
await loadWarehouseList() loading.value = true
await getFunctionSwitch() await Promise.all([
loadWarehouseList(),
getFunctionSwitch(),
getCategoryList(),
getConditionTypeList(),
getOperatorList(),
])
await getSortingRuleList() await getSortingRuleList()
loading.value = false
}) })
</script> </script>
...@@ -340,4 +549,46 @@ onMounted(async () => { ...@@ -340,4 +549,46 @@ onMounted(async () => {
margin-top: 10px; margin-top: 10px;
} }
} }
.conditions-text {
padding: 4px 0;
line-height: 1.6;
overflow: hidden;
.match-type {
color: #666;
margin-bottom: 2px;
}
.condition-item {
display: flex;
align-items: center;
color: #333;
overflow: hidden;
min-width: 0;
.condition-text {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.category-detail-btn {
flex-shrink: 0;
margin-left: 4px;
padding: 0;
height: auto;
}
}
}
</style>
<style lang="scss">
// 详情面板只读:可展开,禁止勾选
.category-cascader-panel-readonly {
.el-checkbox {
pointer-events: none;
}
}
</style> </style>
<template>
<div class="page card h-100 flex-gap-10 overflow-hidden flex flex-column">
<div class="flow-steps">
<div
v-for="item in showFlowConfigList"
:key="item.code"
class="flow-step"
>
{{ item.name }}
</div>
</div>
<div class="flex-1 mt-10">
<el-checkbox v-model="draftSkipInspect">
跳过质检流程 (跳过后生产完成到待配货状态)
</el-checkbox>
</div>
<div class="footer-actions">
<el-button type="primary" @click="updateConfig"> 保存 </el-button>
</div>
</div>
</template>
<script setup lang="ts">
import {
listFactoryOrderFlowConfigApi,
updateFactoryOrderFlowConfigApi,
getFactoryOrderFlowConfigApi,
} from '@/api/order'
import type {
FactoryOrderFlowConfigItem,
FactoryOrderFlowConfig,
} from '@/types/api/order'
const flowConfigList = ref<FactoryOrderFlowConfigItem[]>([])
const factoryOrderFlowConfig = ref<FactoryOrderFlowConfig>()
/** 勾选草稿,仅保存后同步到已生效配置 */
const draftSkipInspect = ref(false)
const showFlowConfigList = computed(() =>
factoryOrderFlowConfig.value?.skipQualityInspect ?? false
? flowConfigList.value.filter((item) => item.code !== 'INSPECTION')
: flowConfigList.value,
)
// 获取质检状态
const getConfig = async () => {
const res = await getFactoryOrderFlowConfigApi()
factoryOrderFlowConfig.value = res.data
draftSkipInspect.value = res.data?.skipQualityInspect ?? false
}
// 获取流程配置列表
const getList = async () => {
try {
const res = await listFactoryOrderFlowConfigApi()
flowConfigList.value = res.data
} catch (e) {
console.error(e)
}
}
// 更新配置
const updateConfig = async () => {
try {
await updateFactoryOrderFlowConfigApi({
id: factoryOrderFlowConfig.value?.id ?? 0,
skipQualityInspect: draftSkipInspect.value,
})
ElMessage.success('更新成功')
await getConfig()
} catch (e) {
console.error(e)
}
}
onMounted(async () => {
await Promise.all([getList(), getConfig()])
})
</script>
<style lang="scss" scoped>
.footer-actions {
display: flex;
justify-content: center;
}
.flow-steps {
display: flex;
height: 40px;
gap: 0px;
}
.flow-step {
flex: none;
width: 150px;
display: flex;
align-items: center;
justify-content: center;
background: #409eff;
color: #fff;
font-size: 14px;
font-weight: 600;
clip-path: polygon(
0 0,
calc(100% - 14px) 0,
100% 50%,
calc(100% - 14px) 100%,
0 100%,
14px 50%
);
&:first-child {
clip-path: polygon(
0 0,
calc(100% - 14px) 0,
100% 50%,
calc(100% - 14px) 100%,
0 100%
);
}
&:last-child {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 14px 50%);
}
}
</style>
...@@ -91,7 +91,6 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) { ...@@ -91,7 +91,6 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
try { try {
const res = await getPodOrderAcceptedStatisticsApi( const res = await getPodOrderAcceptedStatisticsApi(
getQueryPayload(), getQueryPayload(),
currentPage.value,
pageSize.value, pageSize.value,
status.value, status.value,
getListPageAcceptedSubStatus(), getListPageAcceptedSubStatus(),
......
...@@ -2348,6 +2348,13 @@ const baseProductColumns = computed(() => [ ...@@ -2348,6 +2348,13 @@ const baseProductColumns = computed(() => [
align: 'center', align: 'center',
}, },
{ {
key: 'categoryName',
prop: 'categoryName',
label: '商品类目',
minWidth: 160,
align: 'center',
},
{
prop: 'price', prop: 'price',
label: '价格', label: '价格',
minWidth: 100, minWidth: 100,
...@@ -2896,12 +2903,19 @@ const handleSeedingWall = (type: 'print' | 'sort') => { ...@@ -2896,12 +2903,19 @@ const handleSeedingWall = (type: 'print' | 'sort') => {
podOrderVisible.value = true podOrderVisible.value = true
} }
const printOrder = async ( /** LODOP 单例不可并发,打印串行 */
let printOrderChain: Promise<void> = Promise.resolve()
const printOrder = (
data: OrderData, data: OrderData,
callback: (status: boolean) => void, callback: (status: boolean) => void,
) => { ) => {
const run = async () => {
const lodop = getCLodop(null, null) const lodop = getCLodop(null, null)
if (!lodop) return if (!lodop) {
callback && callback(false)
return
}
lodop.PRINT_INIT('打印内容') lodop.PRINT_INIT('打印内容')
const printerIndex = lodop.SET_PRINTER_INDEX(sheetPrinter.value) const printerIndex = lodop.SET_PRINTER_INDEX(sheetPrinter.value)
console.log( console.log(
...@@ -2956,11 +2970,9 @@ const printOrder = async ( ...@@ -2956,11 +2970,9 @@ const printOrder = async (
console.log('PRINT_STATUS_OK:', jobCode, `ok: ${ok}`) console.log('PRINT_STATUS_OK:', jobCode, `ok: ${ok}`)
if (ok == 1) { if (ok == 1) {
console.log('打印成功') console.log('打印成功')
// 打印状态:成功
callback && callback(true) callback && callback(true)
return return
} }
// 如果打印状态表示未成功或者返回了空,继续获取任务是否存在
const exist = await lodopCall((lodop) => const exist = await lodopCall((lodop) =>
lodop.GET_VALUE('PRINT_STATUS_EXIST', jobCode), lodop.GET_VALUE('PRINT_STATUS_EXIST', jobCode),
) )
...@@ -2974,7 +2986,6 @@ const printOrder = async ( ...@@ -2974,7 +2986,6 @@ const printOrder = async (
) )
if (exist == 0) { if (exist == 0) {
console.log('任务不存在了', `exist:${exist}`) console.log('任务不存在了', `exist:${exist}`)
// 任务不存在了
callback && callback(true) callback && callback(true)
return return
} }
...@@ -2991,6 +3002,9 @@ const printOrder = async ( ...@@ -2991,6 +3002,9 @@ const printOrder = async (
lodop.PRINT() lodop.PRINT()
callback && callback(false) callback && callback(false)
} }
}
printOrderChain = printOrderChain.then(run, run)
} }
let _lodop: LODOPObject | null = null let _lodop: LODOPObject | null = null
let _lodopCallback: { [key: string]: (value: string) => void } = {} let _lodopCallback: { [key: string]: (value: string) => void } = {}
......
...@@ -1251,9 +1251,6 @@ const initOrderDetailBox = async (type: 'scan' | 'manual' = 'manual') => { ...@@ -1251,9 +1251,6 @@ const initOrderDetailBox = async (type: 'scan' | 'manual' = 'manual') => {
if (type === 'manual' && boxs.length > 0) { if (type === 'manual' && boxs.length > 0) {
nextTick(async () => { nextTick(async () => {
// 切换箱子至验货完成的箱子
boxChange.value = true
boxIndex.value = boxs[0] as number
const token = ++pickFinishedAlertToken const token = ++pickFinishedAlertToken
if (token > 1) { if (token > 1) {
ElMessageBox.close() ElMessageBox.close()
...@@ -1338,7 +1335,8 @@ const nextStep = async (callback: () => void) => { ...@@ -1338,7 +1335,8 @@ const nextStep = async (callback: () => void) => {
callback && callback() callback && callback()
}) })
} catch (error) { } catch (error) {
productionOrderRef.value.focus() // 取消打单完成时仍允许切换箱子 / 关闭弹窗
callback && callback()
console.error(error) console.error(error)
} }
} else { } else {
...@@ -1448,15 +1446,24 @@ const handlePrinterChange = (value: string) => { ...@@ -1448,15 +1446,24 @@ const handlePrinterChange = (value: string) => {
emit('set-printer', value) emit('set-printer', value)
} }
/** 已自动打印过的订单,避免 WS 回写清掉 printResult 后重复打印 */
const autoPrintedIds = new Set<number>()
const print = (data: OrderData, forcePrint = false, callback?: () => void) => { const print = (data: OrderData, forcePrint = false, callback?: () => void) => {
const _boxIndex = boxIndex.value const _boxIndex = boxIndex.value
const orderId = data.id
if (!forcePrint && data.printResult) { if (
!forcePrint &&
(data.printResult || (orderId != null && autoPrintedIds.has(orderId)))
) {
callback && callback() callback && callback()
return return
} }
if (!forcePrint && orderId != null) autoPrintedIds.add(orderId)
props.printOrder(data, (status: boolean) => { props.printOrder(data, (status: boolean) => {
if (!status && orderId != null) autoPrintedIds.delete(orderId)
callback && callback() callback && callback()
const item = podBoxList.value?.find((item) => item.box === _boxIndex) const item = podBoxList.value?.find((item) => item.box === _boxIndex)
if (item && item.data) { if (item && item.data) {
...@@ -1764,6 +1771,7 @@ const resetPlatform = () => { ...@@ -1764,6 +1771,7 @@ const resetPlatform = () => {
.basic-info-item { .basic-info-item {
display: flex; display: flex;
align-items: center; align-items: center;
overflow: hidden;
span:last-child { span:last-child {
flex: 1; flex: 1;
......
...@@ -901,7 +901,7 @@ ...@@ -901,7 +901,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ElMessage, ElTable, ElTree, TableColumnCtx } from 'element-plus' import { ElMessage, ElTree, TableColumnCtx, type TableInstance } from 'element-plus'
import splitDiv from '@/components/splitDiv/splitDiv.vue' import splitDiv from '@/components/splitDiv/splitDiv.vue'
import { CircleClose, Plus } from '@element-plus/icons-vue' import { CircleClose, Plus } from '@element-plus/icons-vue'
// import pendingReconciliation from './pendingReconciliation.vue' // import pendingReconciliation from './pendingReconciliation.vue'
...@@ -945,7 +945,9 @@ interface Tree { ...@@ -945,7 +945,9 @@ interface Tree {
children?: Tree[] children?: Tree[]
} }
interface SummaryMethodProps<T = AccountStatementNote> { interface SummaryMethodProps<
T extends Record<PropertyKey, any> = AccountStatementNote,
> {
columns: TableColumnCtx<T>[] columns: TableColumnCtx<T>[]
data: T[] data: T[]
} }
...@@ -973,7 +975,7 @@ const selections = ref<AccountStatementNote[]>([]) ...@@ -973,7 +975,7 @@ const selections = ref<AccountStatementNote[]>([])
const detailSelections = ref<ItemList[]>([]) const detailSelections = ref<ItemList[]>([])
const detailList = ref<ItemList[]>([]) const detailList = ref<ItemList[]>([])
const tabsValue = ref<string>('0') const tabsValue = ref<string>('0')
const singleTableRef = ref<InstanceType<typeof ElTable>>() const singleTableRef = ref<TableInstance>()
const currentRow = ref<AccountStatementNote | null>(null) const currentRow = ref<AccountStatementNote | null>(null)
const paymentDialogVisible = ref(false) const paymentDialogVisible = ref(false)
...@@ -1008,7 +1010,7 @@ const { ...@@ -1008,7 +1010,7 @@ const {
refresh: search, refresh: search,
onCurrentPageChange: handleCurrentChange, onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange, onPageSizeChange: handleSizeChange,
} = usePageList({ } = usePageList<AccountStatementNote>({
query: (page, pageSize) => query: (page, pageSize) =>
podReconciliationList( podReconciliationList(
{ {
...@@ -1152,7 +1154,7 @@ const getTreeNum = async () => { ...@@ -1152,7 +1154,7 @@ const getTreeNum = async () => {
console.error(e) console.error(e)
} }
} }
const rowClick = (row: AccountStatementNote) => { const rowClick = (row: AccountStatementNote | null) => {
if (!row) { if (!row) {
currentRow.value = null currentRow.value = null
} }
...@@ -1362,7 +1364,7 @@ const submitConfirmOrder = async (type: number) => { ...@@ -1362,7 +1364,7 @@ const submitConfirmOrder = async (type: number) => {
search() search()
await getTreeNum() await getTreeNum()
if (singleTableRef.value) { if (singleTableRef.value) {
singleTableRef.value!.setCurrentRow(currentRow.value) singleTableRef.value!.setCurrentRow(currentRow.value ?? undefined)
} }
} }
watch( watch(
......
...@@ -34,7 +34,7 @@ const { ...@@ -34,7 +34,7 @@ const {
refresh: search, refresh: search,
onCurrentPageChange: handleCurrentChange, onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange, onPageSizeChange: handleSizeChange,
} = usePageList({ } = usePageList<AccountStatementNote>({
query: (page, pageSize) => query: (page, pageSize) =>
getPodShipmentDetailsById( getPodShipmentDetailsById(
{ {
......
<script setup lang="ts"> <script setup lang="ts">
import { ElMessage, ElTable } from 'element-plus' import { ElMessage } from 'element-plus'
import type { PropType } from 'vue'
import { ProductDetails } from '@/types/api/billOrder.ts' import { ProductDetails } from '@/types/api/billOrder.ts'
const selection = ref<ProductDetails[]>([]) const selection = ref<ProductDetails[]>([])
defineProps({ defineProps({
list: { list: {
type: Array, type: Array as PropType<ProductDetails[]>,
default: () => [], default: () => [],
}, },
}) })
const handleDetailSelectionChange = (arr:ProductDetails[]) => { const handleDetailSelectionChange = (arr: ProductDetails[]) => {
selection.value = arr selection.value = arr
} }
......
...@@ -1040,7 +1040,7 @@ ...@@ -1040,7 +1040,7 @@
<script setup lang="ts"> <script setup lang="ts">
import DetailView from './detail.vue' import DetailView from './detail.vue'
import EditOrder from './editOrder.vue' import EditOrder from './editOrder.vue'
import { ElMessage, ElTable, ElTree, TableColumnCtx } from 'element-plus' import { ElMessage, ElTree, TableColumnCtx, type TableInstance } from 'element-plus'
import splitDiv from '@/components/splitDiv/splitDiv.vue' import splitDiv from '@/components/splitDiv/splitDiv.vue'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
import { useValue } from '@/utils/hooks/useValue' import { useValue } from '@/utils/hooks/useValue'
...@@ -1084,7 +1084,9 @@ interface Tree { ...@@ -1084,7 +1084,9 @@ interface Tree {
children?: Tree[] children?: Tree[]
} }
interface SummaryMethodProps<T = AccountStatementNote> { interface SummaryMethodProps<
T extends Record<PropertyKey, any> = AccountStatementNote,
> {
columns: TableColumnCtx<T>[] columns: TableColumnCtx<T>[]
data: T[] data: T[]
} }
...@@ -1105,7 +1107,7 @@ const orderSelections = ref<OrderDetails[]>([]) ...@@ -1105,7 +1107,7 @@ const orderSelections = ref<OrderDetails[]>([])
const detailList = ref<ProductDetails[]>([]) const detailList = ref<ProductDetails[]>([])
const orderList = ref<OrderDetails[]>([]) const orderList = ref<OrderDetails[]>([])
const tabsValue = ref<string>('0') const tabsValue = ref<string>('0')
const singleTableRef = ref<InstanceType<typeof ElTable>>() const singleTableRef = ref<TableInstance>()
const currentRow = ref<AccountStatementNote | null>(null) const currentRow = ref<AccountStatementNote | null>(null)
const batchSave = async () => { const batchSave = async () => {
...@@ -1182,7 +1184,7 @@ const { ...@@ -1182,7 +1184,7 @@ const {
refresh: search, refresh: search,
onCurrentPageChange: handleCurrentChange, onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange, onPageSizeChange: handleSizeChange,
} = usePageList({ } = usePageList<AccountStatementNote>({
query: (page, pageSize) => query: (page, pageSize) =>
podUsReconciliationList( podUsReconciliationList(
{ {
...@@ -1254,7 +1256,7 @@ const getTreeNum = async () => { ...@@ -1254,7 +1256,7 @@ const getTreeNum = async () => {
console.error(e) console.error(e)
} }
} }
const rowClick = (row: AccountStatementNote) => { const rowClick = (row: AccountStatementNote | null) => {
if (!row) { if (!row) {
currentRow.value = null currentRow.value = null
} }
...@@ -1414,7 +1416,7 @@ const submitConfirmOrder = async (type: number) => { ...@@ -1414,7 +1416,7 @@ const submitConfirmOrder = async (type: number) => {
search() search()
await getTreeNum() await getTreeNum()
if (singleTableRef.value) { if (singleTableRef.value) {
singleTableRef.value!.setCurrentRow(currentRow.value) singleTableRef.value!.setCurrentRow(currentRow.value ?? undefined)
} }
} }
watch( watch(
......
...@@ -34,7 +34,7 @@ const { ...@@ -34,7 +34,7 @@ const {
refresh: search, refresh: search,
onCurrentPageChange: handleCurrentChange, onCurrentPageChange: handleCurrentChange,
onPageSizeChange: handleSizeChange, onPageSizeChange: handleSizeChange,
} = usePageList({ } = usePageList<AccountStatementNote>({
query: (page, pageSize) => query: (page, pageSize) =>
getPodShipmentDetailsById( getPodShipmentDetailsById(
{ {
......
...@@ -894,10 +894,9 @@ ...@@ -894,10 +894,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ElMessage, ElRadioGroup, ElTree } from 'element-plus' import { ElMessage, ElRadioGroup, ElTree, type TableInstance } from 'element-plus'
import { CirclePlusFilled } from '@element-plus/icons-vue' import { CirclePlusFilled } from '@element-plus/icons-vue'
import splitDiv from '@/components/splitDiv/splitDiv.vue' import splitDiv from '@/components/splitDiv/splitDiv.vue'
import { ElTable } from 'element-plus'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
import { checkUpdateParams, AnyObject } from '@/utils/hooks/commonUtil' import { checkUpdateParams, AnyObject } from '@/utils/hooks/commonUtil'
import { useValue } from '@/utils/hooks/useValue' import { useValue } from '@/utils/hooks/useValue'
...@@ -1064,7 +1063,7 @@ const tradingTime = ref<string[]>([]) ...@@ -1064,7 +1063,7 @@ const tradingTime = ref<string[]>([])
const selections = ref<InterWarehousePage[]>([]) const selections = ref<InterWarehousePage[]>([])
const detailList = ref<InterProductList[]>([]) const detailList = ref<InterProductList[]>([])
const tabsValue = ref<string>('0') const tabsValue = ref<string>('0')
const singleTableRef = ref<InstanceType<typeof ElTable>>() const singleTableRef = ref<TableInstance>()
const currentRow = ref<InterWarehousePage | null>(null) const currentRow = ref<InterWarehousePage | null>(null)
const logList = ref<LogListData[]>([]) const logList = ref<LogListData[]>([])
const rules = { const rules = {
...@@ -1399,7 +1398,7 @@ const getWarehouseList = async () => { ...@@ -1399,7 +1398,7 @@ const getWarehouseList = async () => {
console.error(e) console.error(e)
} }
} }
const rowClick = (row: InterWarehousePage) => { const rowClick = (row: InterWarehousePage | null) => {
if (!row) { if (!row) {
currentRow.value = null currentRow.value = null
} }
......
...@@ -629,9 +629,8 @@ ...@@ -629,9 +629,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ElMessage, ElRadioGroup, ElTree } from 'element-plus' import { ElMessage, ElRadioGroup, ElTree, type TableInstance } from 'element-plus'
import splitDiv from '@/components/splitDiv/splitDiv.vue' import splitDiv from '@/components/splitDiv/splitDiv.vue'
import { ElTable } from 'element-plus'
import ReceiptProductDialog from './components/ReceiptProductDialog.vue' import ReceiptProductDialog from './components/ReceiptProductDialog.vue'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
import { checkUpdateParams, AnyObject } from '@/utils/hooks/commonUtil' import { checkUpdateParams, AnyObject } from '@/utils/hooks/commonUtil'
...@@ -807,7 +806,7 @@ const tradingTime = ref<string[]>([]) ...@@ -807,7 +806,7 @@ const tradingTime = ref<string[]>([])
const selections = ref<InterWarehousePage[]>([]) const selections = ref<InterWarehousePage[]>([])
const detailList = ref<InterProductList[]>([]) const detailList = ref<InterProductList[]>([])
const tabsValue = ref<string>('0') const tabsValue = ref<string>('0')
const singleTableRef = ref<InstanceType<typeof ElTable>>() const singleTableRef = ref<TableInstance>()
const currentRow = ref<InterWarehousePage | null>(null) const currentRow = ref<InterWarehousePage | null>(null)
const logList = ref<LogListData[]>([]) const logList = ref<LogListData[]>([])
const detailLoading = ref(false) const detailLoading = ref(false)
...@@ -1085,7 +1084,7 @@ const getWarehouseList = async () => { ...@@ -1085,7 +1084,7 @@ const getWarehouseList = async () => {
console.error(e) console.error(e)
} }
} }
const rowClick = (row: InterWarehousePage) => { const rowClick = (row: InterWarehousePage | null) => {
if (!row) { if (!row) {
currentRow.value = null currentRow.value = null
} }
......
...@@ -998,10 +998,9 @@ ...@@ -998,10 +998,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ElMessage, ElRadioGroup, ElTree } from 'element-plus' import { ElMessage, ElRadioGroup, ElTree, type TableInstance } from 'element-plus'
import { CirclePlusFilled } from '@element-plus/icons-vue' import { CirclePlusFilled } from '@element-plus/icons-vue'
import splitDiv from '@/components/splitDiv/splitDiv.vue' import splitDiv from '@/components/splitDiv/splitDiv.vue'
import { ElTable } from 'element-plus'
import usePageList from '@/utils/hooks/usePageList' import usePageList from '@/utils/hooks/usePageList'
import { checkUpdateParams, AnyObject } from '@/utils/hooks/commonUtil' import { checkUpdateParams, AnyObject } from '@/utils/hooks/commonUtil'
import { useValue } from '@/utils/hooks/useValue' import { useValue } from '@/utils/hooks/useValue'
...@@ -1174,7 +1173,7 @@ const tradingTime = ref<string[]>([]) ...@@ -1174,7 +1173,7 @@ const tradingTime = ref<string[]>([])
const selections = ref<InterWarehousePage[]>([]) const selections = ref<InterWarehousePage[]>([])
const detailList = ref<InterProductList[]>([]) const detailList = ref<InterProductList[]>([])
const tabsValue = ref<string>('0') const tabsValue = ref<string>('0')
const singleTableRef = ref<InstanceType<typeof ElTable>>() const singleTableRef = ref<TableInstance>()
const currentRow = ref<InterWarehousePage | null>(null) const currentRow = ref<InterWarehousePage | null>(null)
const logList = ref<LogListData[]>([]) const logList = ref<LogListData[]>([])
const rules = { const rules = {
...@@ -1509,7 +1508,7 @@ const getWarehouseList = async () => { ...@@ -1509,7 +1508,7 @@ const getWarehouseList = async () => {
console.error(e) console.error(e)
} }
} }
const rowClick = (row: InterWarehousePage) => { const rowClick = (row: InterWarehousePage | null) => {
if (!row) { if (!row) {
currentRow.value = null currentRow.value = null
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment