From 4496d865a5aeaa454b113391b768b68b3f42fb90 Mon Sep 17 00:00:00 2001 From: dzc <2388224167@qq.com> Date: Wed, 21 Jan 2026 09:35:50 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E4=BF=AE=E6=94=B9bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 156 ++++--- .prettierrc.js | 74 +-- dict.js | 66 +-- src/api/device/deviceGroup.ts | 34 ++ .../DraggableDeviceGroupDraggable/index.vue | 431 ++++++++++++++++++ src/components/Select/SelectTreeChannel.vue | 22 +- src/components/index.ts | 2 + src/views/alarm/setting/panel/index.vue | 21 +- .../device/components/preview-video/index.vue | 20 +- .../device/components/replay-video/index.vue | 2 + .../deviceGroup/camera-table-dialog.vue | 30 +- .../deviceGroup/camera-table-form-dialog.vue | 113 +++++ src/views/device/deviceGroup/form.vue | 19 +- src/views/device/deviceGroupPlay/index.vue | 25 +- .../camera-table-dialog.vue | 211 +++++++++ .../device/devicePlaybackGroup/index.vue | 98 ++++ 16 files changed, 1146 insertions(+), 178 deletions(-) create mode 100644 src/components/DraggableDeviceGroupDraggable/index.vue create mode 100644 src/views/device/deviceGroup/camera-table-form-dialog.vue create mode 100644 src/views/device/devicePlaybackGroup/camera-table-dialog.vue create mode 100644 src/views/device/devicePlaybackGroup/index.vue diff --git a/.eslintrc.js b/.eslintrc.js index bf6223b..b715bdf 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,78 +1,82 @@ export default { - root: true, - env: { - browser: true, - es2021: true, - node: true, - }, - parser: 'vue-eslint-parser', - parserOptions: { - ecmaVersion: 12, - parser: '@typescript-eslint/parser', - sourceType: 'module', - }, - extends: ['plugin:vue/vue3-essential', 'plugin:vue/essential', 'eslint:recommended'], - plugins: ['vue', '@typescript-eslint'], - overrides: [ - { - files: ['*.ts', '*.tsx', '*.vue'], - rules: { - 'no-undef': 'off', - }, - }, - ], - rules: { - // http://eslint.cn/docs/rules/ - // https://eslint.vuejs.org/rules/ - // https://typescript-eslint.io/rules/no-unused-vars/ - '@typescript-eslint/ban-ts-ignore': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-var-requires': 'off', - '@typescript-eslint/no-empty-function': 'off', - '@typescript-eslint/no-use-before-define': 'off', - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/ban-types': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-redeclare': 'error', - '@typescript-eslint/no-non-null-asserted-optional-chain': 'off', - '@typescript-eslint/no-unused-vars': 'off', - 'vue/custom-event-name-casing': 'off', - 'vue/attributes-order': 'off', - 'vue/one-component-per-file': 'off', - 'vue/html-closing-bracket-newline': 'off', - 'vue/max-attributes-per-line': 'off', - 'vue/multiline-html-element-content-newline': 'off', - 'vue/singleline-html-element-content-newline': 'off', - 'vue/attribute-hyphenation': 'off', - 'vue/html-self-closing': 'off', - 'vue/no-multiple-template-root': 'off', - 'vue/require-default-prop': 'off', - 'vue/no-v-model-argument': 'off', - 'vue/no-arrow-functions-in-watch': 'off', - 'vue/no-template-key': 'off', - 'vue/no-v-html': 'off', - 'vue/comment-directive': 'off', - 'vue/no-mutating-props': 'off', - 'vue/no-parsing-error': 'off', - 'vue/no-deprecated-v-on-native-modifier': 'off', - 'vue/multi-word-component-names': 'off', - 'no-useless-escape': 'off', - 'no-sparse-arrays': 'off', - 'no-prototype-builtins': 'off', - 'no-constant-condition': 'off', - 'no-use-before-define': 'off', - 'no-restricted-globals': 'off', - 'no-restricted-syntax': 'off', - 'generator-star-spacing': 'off', - 'no-unreachable': 'off', - 'no-multiple-template-root': 'off', - 'no-unused-vars': 'off', - 'no-v-model-argument': 'off', - 'no-case-declarations': 'off', - 'no-console': 'error', - 'no-redeclare': 'off', - 'no-mixed-spaces-and-tabs': 'off', - }, + root: true, + env: { + browser: true, + es2021: true, + node: true, + }, + parser: "vue-eslint-parser", + parserOptions: { + ecmaVersion: 12, + parser: "@typescript-eslint/parser", + sourceType: "module", + }, + extends: [ + "plugin:vue/vue3-essential", + "plugin:vue/essential", + "eslint:recommended", + ], + plugins: ["vue", "@typescript-eslint"], + overrides: [ + { + files: ["*.ts", "*.tsx", "*.vue"], + rules: { + "no-undef": "off", + }, + }, + ], + rules: { + // http://eslint.cn/docs/rules/ + // https://eslint.vuejs.org/rules/ + // https://typescript-eslint.io/rules/no-unused-vars/ + "@typescript-eslint/ban-ts-ignore": "off", + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-var-requires": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-use-before-define": "off", + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/no-redeclare": "error", + "@typescript-eslint/no-non-null-asserted-optional-chain": "off", + "@typescript-eslint/no-unused-vars": "off", + "vue/custom-event-name-casing": "off", + "vue/attributes-order": "off", + "vue/one-component-per-file": "off", + "vue/html-closing-bracket-newline": "off", + "vue/max-attributes-per-line": "off", + "vue/multiline-html-element-content-newline": "off", + "vue/singleline-html-element-content-newline": "off", + "vue/attribute-hyphenation": "off", + "vue/html-self-closing": "off", + "vue/no-multiple-template-root": "off", + "vue/require-default-prop": "off", + "vue/no-v-model-argument": "off", + "vue/no-arrow-functions-in-watch": "off", + "vue/no-template-key": "off", + "vue/no-v-html": "off", + "vue/comment-directive": "off", + "vue/no-mutating-props": "off", + "vue/no-parsing-error": "off", + "vue/no-deprecated-v-on-native-modifier": "off", + "vue/multi-word-component-names": "off", + "no-useless-escape": "off", + "no-sparse-arrays": "off", + "no-prototype-builtins": "off", + "no-constant-condition": "off", + "no-use-before-define": "off", + "no-restricted-globals": "off", + "no-restricted-syntax": "off", + "generator-star-spacing": "off", + "no-unreachable": "off", + "no-multiple-template-root": "off", + "no-unused-vars": "off", + "no-v-model-argument": "off", + "no-case-declarations": "off", + "no-console": "error", + "no-redeclare": "off", + "no-mixed-spaces-and-tabs": "off", + }, }; diff --git a/.prettierrc.js b/.prettierrc.js index 877ae35..b0f500d 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,39 +1,39 @@ export default { - // 一行最多多少个字符 - printWidth: 150, - // 指定每个缩进级别的空格数 - tabWidth: 2, - // 使用制表符而不是空格缩进行 - useTabs: true, - // 在语句末尾打印分号 - semi: true, - // 使用单引号而不是双引号 - singleQuote: true, - // 更改引用对象属性的时间 可选值"" - quoteProps: 'as-needed', - // 在JSX中使用单引号而不是双引号 - jsxSingleQuote: false, - // 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"",默认none - trailingComma: 'es5', - // 在对象文字中的括号之间打印空格 - bracketSpacing: true, - // jsx 标签的反尖括号需要换行 - jsxBracketSameLine: false, - // 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x - arrowParens: 'always', - // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码 - rangeStart: 0, - rangeEnd: Infinity, - // 指定要使用的解析器,不需要写文件开头的 @prettier - requirePragma: false, - // 不需要自动在文件开头插入 @prettier - insertPragma: false, - // 使用默认的折行标准 always\never\preserve - proseWrap: 'preserve', - // 指定HTML文件的全局空格敏感度 css\strict\ignore - htmlWhitespaceSensitivity: 'css', - // Vue文件脚本和样式标签缩进 - vueIndentScriptAndStyle: false, - // 换行符使用 lf 结尾是 可选值"" - endOfLine: 'lf', + // 一行最多多少个字符 + printWidth: 150, + // 指定每个缩进级别的空格数 + tabWidth: 2, + // 使用制表符而不是空格缩进行 + useTabs: true, + // 在语句末尾打印分号 + semi: true, + // 使用单引号而不是双引号 + singleQuote: true, + // 更改引用对象属性的时间 可选值"" + quoteProps: "as-needed", + // 在JSX中使用单引号而不是双引号 + jsxSingleQuote: false, + // 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"",默认none + trailingComma: "es5", + // 在对象文字中的括号之间打印空格 + bracketSpacing: true, + // jsx 标签的反尖括号需要换行 + jsxBracketSameLine: false, + // 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x + arrowParens: "always", + // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码 + rangeStart: 0, + rangeEnd: Infinity, + // 指定要使用的解析器,不需要写文件开头的 @prettier + requirePragma: false, + // 不需要自动在文件开头插入 @prettier + insertPragma: false, + // 使用默认的折行标准 always\never\preserve + proseWrap: "preserve", + // 指定HTML文件的全局空格敏感度 css\strict\ignore + htmlWhitespaceSensitivity: "css", + // Vue文件脚本和样式标签缩进 + vueIndentScriptAndStyle: false, + // 换行符使用 lf 结尾是 可选值"" + endOfLine: "lf", }; diff --git a/dict.js b/dict.js index ef7d834..9caea10 100644 --- a/dict.js +++ b/dict.js @@ -1,35 +1,35 @@ (function () { - const result = [] - const dictId = "1970693001990479873" - const dictType = "alarm_type" - let children = document.querySelector("#doc").children; - if (children) { - children = Array.from(children) + const result = []; + const dictId = "1970693001990479873"; + const dictType = "alarm_type"; + let children = document.querySelector("#doc").children; + if (children) { + children = Array.from(children); + } + if (children && children.length) { + children.forEach((dom) => { + const tdList = dom.querySelectorAll("td"); + const id = tdList[0]; + const desc = tdList[1]; + result.push({ + id: id?.innerText, + desc: desc?.innerText, + }); + }); + } + // 生产sql + let fullSql = ``; + if (result && result.length) { + let sort = 10; + for (const v of result) { + // 随机id 当前时间戳 + 6位数数字随机 + const id = new Date().getTime() + Math.floor(Math.random() * 1000000); + const sql = ` + INSERT INTO jhkjx.sys_dict_item (id, dict_id, item_value, label, dict_type, description, sort_order, create_by, update_by, create_time, update_time, remarks, del_flag, tenant_id) VALUES (${id}, ${dictId}, '${v.id}', '${v.desc}', '${dictType}', '${v.desc}', ${sort}, ' ', ' ', '2019-03-19 11:08:59', '2019-03-25 12:49:13', '', '0', 1);`; + // 换行 + fullSql = fullSql + sql + "\n"; + sort += 10; } - if (children && children.length) { - children.forEach((dom) => { - const tdList = dom.querySelectorAll("td") - const id = tdList[0] - const desc = tdList[1] - result.push({ - id: id?.innerText, - desc: desc?.innerText - }) - }); - } - // 生产sql - let fullSql = `` - if (result && result.length) { - let sort = 10 - for (const v of result) { - // 随机id 当前时间戳 + 6位数数字随机 - const id = new Date().getTime() + Math.floor(Math.random() * 1000000) - const sql = ` - INSERT INTO jhkjx.sys_dict_item (id, dict_id, item_value, label, dict_type, description, sort_order, create_by, update_by, create_time, update_time, remarks, del_flag, tenant_id) VALUES (${id}, ${dictId}, '${v.id}', '${v.desc}', '${dictType}', '${v.desc}', ${sort}, ' ', ' ', '2019-03-19 11:08:59', '2019-03-25 12:49:13', '', '0', 1);` - // 换行 - fullSql = fullSql + sql + "\n" - sort += 10 - } - } - console.log(fullSql); -})(); \ No newline at end of file + } + console.log(fullSql); +})(); diff --git a/src/api/device/deviceGroup.ts b/src/api/device/deviceGroup.ts index a6c4a8a..375588f 100644 --- a/src/api/device/deviceGroup.ts +++ b/src/api/device/deviceGroup.ts @@ -22,6 +22,40 @@ export function createGroupItem(data: any) { }) } +/** + * 添加设施分组子项 + */ +export function createGroupItems(data: any) { + return request({ + url: '/admin/device/deviceGroup/createGroupItems', + method: 'post', + data: data + }) +} + +/** + * 添加设施分组子项 + */ +export function editGroupItem(data: any) { + return request({ + url: '/admin/device/deviceGroup/editGroupItem', + method: 'post', + data: data + }) +} + +/** + * 添加设施分组子项 + */ +export function groupItemDetail(params: any) { + return request({ + url: '/admin/device/deviceGroup/groupItemDetail', + method: 'get', + params: params + }) +} + + /** * 删除设施分组子项 */ diff --git a/src/components/DraggableDeviceGroupDraggable/index.vue b/src/components/DraggableDeviceGroupDraggable/index.vue new file mode 100644 index 0000000..913e1c1 --- /dev/null +++ b/src/components/DraggableDeviceGroupDraggable/index.vue @@ -0,0 +1,431 @@ + + + + + diff --git a/src/components/Select/SelectTreeChannel.vue b/src/components/Select/SelectTreeChannel.vue index 4535ce7..7c671b2 100644 --- a/src/components/Select/SelectTreeChannel.vue +++ b/src/components/Select/SelectTreeChannel.vue @@ -4,7 +4,19 @@ children: 'deviceChannelList', }" node-key="treeValue" v-model="state.selectId" :placeholder="props.placeholder" :clearable="props.clearable" :loading="state.loading" :data="state.options" :render-after-expand="false" show-checkbox multiple - style="width: 100%" filterable default-expand-all @visible-change="(e: boolean) => e && getOptions()" /> + style="width: 100%" filterable default-expand-all @visible-change="(e: boolean) => e && getOptions()"> + + \ No newline at end of file + diff --git a/src/views/device/components/preview-video/index.vue b/src/views/device/components/preview-video/index.vue index cceb75c..4be8d3c 100644 --- a/src/views/device/components/preview-video/index.vue +++ b/src/views/device/components/preview-video/index.vue @@ -15,6 +15,8 @@ import PlayerManager from "../../components/lib/icc/PlayerManager"; // @ts-ignore import { rendererVideoPage } from "../lib/custom/video-custom" +const emit = defineEmits(["windowNumChanged"]) + let realPlayer: any = null; const state = reactive({ @@ -28,13 +30,14 @@ const state = reactive({ * 2=辅码流 * 3=辅码流2 */ - streamType: 1, + streamType: 2, current: 1, total: 100, }) const query = reactive({ list: [], + deviceMap: {}, deviceCode: null, }) @@ -43,7 +46,7 @@ onUnmounted(() => { }) // 视频预览初始化 -async function init(channelCodelist: any, deviceCode: any) { +async function init(channelCodelist: any, deviceCode: any, deviceMap: any) { try { destroy() if (channelCodelist) { @@ -52,6 +55,11 @@ async function init(channelCodelist: any, deviceCode: any) { if (deviceCode) { query.deviceCode = deviceCode } + if (deviceMap) { + query.deviceMap = deviceMap + } else { + query.deviceMap = {} + } state.current = 1 state.num = state.numList[state.numList.length - 1] refreshPage() @@ -65,16 +73,15 @@ async function initVideo() { const _params = { deviceChannelCodeList: query.list ?? [], deviceCode: query.deviceCode ?? null, + deviceTreeMap: query.deviceMap ?? null, current: state.current, size: state.num, }; try { let { code, data } = await DeviceChannelApi.getChannelPage(_params); if (code != 0) throw new Error(); - let { records, total } = data + let { records = [], total } = data if (!records || !records.length) records = [] - console.log("page total:", total); - state.total = total let suitableNum = state.num if (state.total < state.numList[state.numList.length - 1]) { @@ -148,7 +155,7 @@ function initPlayerManager() { break; // 播放器显示的路数发生改变 case "windowNumChanged": - console.log("每页", data); + emit("windowNumChanged", data) if (data == state.num) return state.current = 1 state.num = data @@ -223,7 +230,6 @@ function refreshPage() { return state.total }, onChange: (page: any, pageSize: any) => { - console.log("分页", page, pageSize); if (page == state.current && pageSize == state.num) { return } diff --git a/src/views/device/components/replay-video/index.vue b/src/views/device/components/replay-video/index.vue index 655aa89..21d17b8 100644 --- a/src/views/device/components/replay-video/index.vue +++ b/src/views/device/components/replay-video/index.vue @@ -11,6 +11,7 @@ import { formatDateRange } from "/@/utils/formatTime" import * as AlarmReplayVideoListApi from "/@/api/third/deviceCameraReplay" import * as DeviceChannelApi from "/@/api/device/deviceChannel" +const emit = defineEmits(["windowNumChanged"]) // @ts-ignore import PlayerManager from "../lib/icc/PlayerManager"; @@ -143,6 +144,7 @@ function initPlayerManager() { break; // 播放器显示的路数发生改变 case 'windowNumChanged': + emit("windowNumChanged", data) if (state.currentNum == data) return state.currentNum = data if (replayVideo) { diff --git a/src/views/device/deviceGroup/camera-table-dialog.vue b/src/views/device/deviceGroup/camera-table-dialog.vue index 1b0704c..5742587 100644 --- a/src/views/device/deviceGroup/camera-table-dialog.vue +++ b/src/views/device/deviceGroup/camera-table-dialog.vue @@ -25,7 +25,7 @@ :header-cell-style="tableStyle.headerCellStyle" @selection-change="selectionChangHandle" @sort-change="sortChangeHandle"> - + @@ -37,8 +37,24 @@ + + + + + + @@ -51,7 +67,8 @@ - + + @@ -63,6 +80,8 @@ + + \ No newline at end of file diff --git a/src/views/device/deviceGroup/form.vue b/src/views/device/deviceGroup/form.vue index 61b3a4f..a26ea29 100644 --- a/src/views/device/deviceGroup/form.vue +++ b/src/views/device/deviceGroup/form.vue @@ -9,7 +9,7 @@ - + @@ -55,18 +55,19 @@ const loading = ref(false) // 定义字典 const { panel_type } = useDict("panel_type") // 提交表单数据 -const form = reactive({ +const defaultForm = { id: '', name: '', bizType: '', panelViewCodes: [], - sort: '', -}); + sort: 10, +} +const form = reactive({ ...defaultForm }); // 定义校验规则 const dataRules = ref({ name: [{ required: true, message: '分组名称不能为空', trigger: 'blur' }], - bizType: [{ required: true, message: '类型不能为空', trigger: 'blur' }], + panelViewCodes: [{ required: true, message: '类型不能为空', trigger: 'change' }], sort: [{ required: true, message: '排序不能为空', trigger: 'blur' }], }) @@ -76,10 +77,8 @@ const openDialog = (id: string) => { form.id = '' // 重置表单数据 - nextTick(() => { - dataFormRef.value?.resetFields(); - }); - + dataFormRef.value?.resetFields(); + Object.assign(form, defaultForm) // 获取deviceGroup信息 if (id) { form.id = id @@ -119,6 +118,8 @@ const getdeviceGroupData = async (id: string) => { const { code, data } = await deviceGroupApi.getObj(id) if (data.panelViewCodes) { data.panelViewCodes = data.panelViewCodes.split(',') + } else { + data.panelViewCodes = [] } Object.assign(form, data) } catch (error) { diff --git a/src/views/device/deviceGroupPlay/index.vue b/src/views/device/deviceGroupPlay/index.vue index 0f2bb69..13a139b 100644 --- a/src/views/device/deviceGroupPlay/index.vue +++ b/src/views/device/deviceGroupPlay/index.vue @@ -1,16 +1,17 @@ @@ -85,21 +87,27 @@ const data = { const state = reactive({ currentTab: data.tabs[0].name, + deviceTreeMap: {}, channelCodeList: [], replayForm: { streamType: "1", recordSource: "2", videoDateList: getDefaultDateRange() }, - dataList: [] + dataList: [], + windowNum: null }) onMounted(() => { }) +const windowNumChanged = (num: any) => { + state.windowNum = num +} + // 树节点选择时 -const handleGetSelectNode = async (curClickedNode: any, checkedTreeNodes: any, isDeepTree: boolean) => { +const handleGetSelectNode = async (curClickedNode: any, checkedTreeNodes: any, isDeepTree: boolean, selectMap: any) => { if (!checkedTreeNodes || !checkedTreeNodes.length) return const list = checkedTreeNodes.filter((v: any) => v.deep == 2).map((v: any) => v.code) if (!list || !list.length) { @@ -107,6 +115,7 @@ const handleGetSelectNode = async (curClickedNode: any, checkedTreeNodes: any, i return; } state.channelCodeList = list + state.deviceTreeMap = selectMap init(state.currentTab) } @@ -120,7 +129,7 @@ function init(type: string) { await new Promise(resolve => setTimeout(resolve, 500)) switch (type) { case 'PREVIEW': - previewVideoRef.value.init(state.channelCodeList) + previewVideoRef.value.init(state.channelCodeList, null, state.deviceTreeMap) break; case 'DOOR': await getDataList() diff --git a/src/views/device/devicePlaybackGroup/camera-table-dialog.vue b/src/views/device/devicePlaybackGroup/camera-table-dialog.vue new file mode 100644 index 0000000..5742587 --- /dev/null +++ b/src/views/device/devicePlaybackGroup/camera-table-dialog.vue @@ -0,0 +1,211 @@ + + + + + \ No newline at end of file diff --git a/src/views/device/devicePlaybackGroup/index.vue b/src/views/device/devicePlaybackGroup/index.vue new file mode 100644 index 0000000..dc0dbb4 --- /dev/null +++ b/src/views/device/devicePlaybackGroup/index.vue @@ -0,0 +1,98 @@ + + + \ No newline at end of file