From 139ddd5a67d06b9bd7e42295a832b0a05d0ee263 Mon Sep 17 00:00:00 2001 From: lzq Date: Sat, 2 Aug 2025 11:40:06 +0800 Subject: [PATCH] 1 --- .env | 6 +- package-lock.json | 17 ++ package.json | 1 + .../{iconfont-dts.ts => iconfont-process.ts} | 14 +- src/common/app/app-api.ts | 14 +- src/common/index.ts | 2 +- src/common/utils/http-util.ts | 68 +++--- src/dts/components.d.ts | 3 + src/pages/dispose-recode/DisposeRecode.vue | 7 +- .../dispose-recode/DisposeRecodeDetail.vue | 4 - src/pages/frame/ZsyFrame.vue | 10 +- src/pages/main-zone/header-bar/HeaderBar.vue | 4 +- src/pages/main-zone/header-bar/UserPanel.vue | 4 +- .../sys/menus/menu-create/MenuCreate.vue | 2 +- src/pages/sys/menus/menu-form/MenuForm.vue | 72 ++++++ src/pages/sys/menus/menu.d.ts | 4 +- src/pages/sys/user/User.vue | 5 +- src/pages/sys/user/user-form/UserForm.vue | 12 +- src/pages/tsp/Tsp.vue | 21 +- src/pages/tsp/VideoPanel.vue | 224 ++++++++++++++++++ src/pages/tsp/tsp.d.ts | 1 + vite.config.ts | 9 +- 22 files changed, 426 insertions(+), 78 deletions(-) rename plugin/{iconfont-dts.ts => iconfont-process.ts} (53%) create mode 100644 src/pages/tsp/VideoPanel.vue diff --git a/.env b/.env index 14a8a3f..87a241d 100644 --- a/.env +++ b/.env @@ -4,9 +4,9 @@ VITE_APP_NAME=垃圾回收监管平台 # 服务器基础地址 VITE_HTTP_SERVER_BASE_URL=/api VITE_WS_SERVER_BASE_URL=/ws -VITE_OSS_UPLOAD_BASE_URL=http://218.94.108.114:19000/iot -VITE_OSS_DOWNLOAD_BASE_URL=/api/file/oss/download -VITE_OSS_BUCKET_NAME=iot +VITE_OSS_UPLOAD_BASE_URL=http://localhost:9000/zsy +VITE_OSS_DOWNLOAD_BASE_URL=/api/oss/download +VITE_OSS_BUCKET_NAME=zsy # 接口超时时间 VITE_HTTP_SERVER_TIMEOUT=10000 diff --git a/package-lock.json b/package-lock.json index 726122d..dadb9c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "axios": "1.7.4", "decimal.js": "^10.4.3", "echarts": "^6.0.0", + "flv.js": "^1.6.2", "gridstack": "^12.0.0", "logan-web": "^1.1.0", "luxon": "^3.4.4", @@ -3267,6 +3268,16 @@ "node": ">=8" } }, + "node_modules/flv.js": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/flv.js/-/flv.js-1.6.2.tgz", + "integrity": "sha512-xre4gUbX1MPtgQRKj2pxJENp/RnaHaxYvy3YToVVCrSmAWUu85b9mug6pTXF6zakUjNP2lFWZ1rkSX7gxhB/2A==", + "license": "Apache-2.0", + "dependencies": { + "es6-promise": "^4.2.8", + "webworkify-webpack": "^2.1.5" + } + }, "node_modules/follow-redirects": { "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", @@ -6559,6 +6570,12 @@ "dev": true, "license": "MIT" }, + "node_modules/webworkify-webpack": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/webworkify-webpack/-/webworkify-webpack-2.1.5.tgz", + "integrity": "sha512-2akF8FIyUvbiBBdD+RoHpoTbHMQF2HwjcxfDvgztAX5YwbZNyrtfUMgvfgFVsgDhDPVTlkbb5vyasqDHfIDPQw==", + "license": "MIT" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 1b2a990..8001e49 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "axios": "1.7.4", "decimal.js": "^10.4.3", "echarts": "^6.0.0", + "flv.js": "^1.6.2", "gridstack": "^12.0.0", "logan-web": "^1.1.0", "luxon": "^3.4.4", diff --git a/plugin/iconfont-dts.ts b/plugin/iconfont-process.ts similarity index 53% rename from plugin/iconfont-dts.ts rename to plugin/iconfont-process.ts index 5654bfc..3baca55 100644 --- a/plugin/iconfont-dts.ts +++ b/plugin/iconfont-process.ts @@ -11,11 +11,12 @@ interface IconfontJson { }[]; } -export default function iconfontDts(outFile: string) { +export default function iconfontProcess(outPath: string) { return function (content: string) { const json = JSON.parse(content) as IconfontJson const names = json.glyphs.map(glyph => glyph.font_class) - console.log('正在生成文件:', outFile) + const dtsFile = outPath + '/iconfont.d.ts' + console.log('正在生成文件:', dtsFile) const dts = `export {} declare global { @@ -24,8 +25,13 @@ declare global { } } ` - fs.writeFileSync(outFile, dts, {encoding: 'utf-8'}) - console.log('文件生成完成') + fs.writeFileSync(dtsFile, dts, {encoding: 'utf-8'}) + const tsFile = outPath + '/icons.ts' + const ts = `export default reactive([${'\n ' + names.map(name => `{name: '${name}'}`).join(',\n ') + '\n'}])` + console.log('正在生成文件:', tsFile) + fs.writeFileSync(tsFile, ts, {encoding: 'utf-8'}) + + console.log('文件生成完成') } } diff --git a/src/common/app/app-api.ts b/src/common/app/app-api.ts index 1f19bf1..9934c7e 100644 --- a/src/common/app/app-api.ts +++ b/src/common/app/app-api.ts @@ -1,5 +1,8 @@ -import { get } from '@/common/utils/http-util.ts' -import { bucketName } from '@/common' +import { + get, + getFileUrl +} from '@/common/utils/http-util.ts' +import { bucketName, } from '@/common' interface PresignedUrl extends Record { bucketName?: string @@ -8,9 +11,12 @@ interface PresignedUrl extends Record { export default { obtainPresignedUrl(filename: string) { - return get('/file/oss/obtain_presigned_url', {filename, bucketName}) + return get('/oss/obtain_presigned_url', {filename, bucketName}) }, download(objectName: string) { - return get('/file/oss/download/' + objectName) + return get('/oss/download/' + objectName) }, + fileUrl(filename: string) { + return getFileUrl(filename) + } } diff --git a/src/common/index.ts b/src/common/index.ts index f0e47d7..cbb67bd 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -14,7 +14,7 @@ export const appName = import.meta.env.VITE_APP_NAME export const serverBaseUrl = import.meta.env.VITE_HTTP_SERVER_BASE_URL ?? '/' export const uploadBaseUrl = import.meta.env.VITE_OSS_UPLOAD_BASE_URL ?? '/' export const downloadBaseUrl = import.meta.env.VITE_OSS_DOWNLOAD_BASE_URL ?? '/' -export const bucketName = import.meta.env.VITE_OSS_BUCKET_NAME ?? 'iot' +export const bucketName = import.meta.env.VITE_OSS_BUCKET_NAME ?? 'zsy' /** * Axios 超时时间 diff --git a/src/common/utils/http-util.ts b/src/common/utils/http-util.ts index b2164dd..926c366 100644 --- a/src/common/utils/http-util.ts +++ b/src/common/utils/http-util.ts @@ -1,4 +1,5 @@ import { + downloadBaseUrl, serverBaseUrl, serverTimeout } from '@/common' @@ -6,6 +7,7 @@ import axios, { AxiosRequestConfig } from 'axios' import * as qs from 'qs' import Toast from '@/components/toast' import { useAppUserStore } from '@/common/app/app-user-store.ts' +import { throttle } from '@/common/utils/index.ts' /** * HTTP 统一响应结构 @@ -41,24 +43,13 @@ const paramsSerializer = (params: any) => { /** * 统一错误处理函数 - * - * @param code 响应码 - * @param msg 响应信息 - * @param message 响应信息(详细) */ -function errHandler({code, msg, message}: R) { - switch (code) { - case 450401: - console.log(msg, message) - break - case 450403: - console.log(msg, message) - break - default: - Toast.error(message ?? '操作失败') - } -} - +/* function errHandler(r?: R) { + Toast.error(r?.message ?? '操作失败') + } */ +const errHandler = throttle(500, (r?: R) => { + Toast.error(r?.message ?? '操作失败') +}) /** * axios 实例 */ @@ -98,7 +89,7 @@ httpUtil.interceptors.response.use( return Promise.resolve(response) } if (response.data == null) { - response.data = {code: 99999, msg: '无响应内容', message: '无响应内容', data: null, headers: response.headers} + response.data = {code: 0, msg: '无响应内容', message: '无响应内容', data: null, headers: response.headers} } response.data.headers = response.headers if (response.data.code === 0) { @@ -113,14 +104,14 @@ httpUtil.interceptors.response.use( error.response.data = {...error.response.data, headers: error.response.headers} } else if (error.request != null) { error.response = { - data: {code: 99999, msg: '服务器未响', message: '服务器未响', data: null}, + data: {code: 9999, msg: '网络异常', message: '网络异常', data: null}, } } else { error.response = { - data: {code: 55555, msg: '请求发送失败', message: '请求发送失败', data: null}, + data: {code: 5555, msg: '请求发送失败', message: '请求发送失败', data: null}, } } - return Promise.reject(error) + return Promise.reject(error.response.data) }, ) @@ -132,11 +123,11 @@ httpUtil.interceptors.response.use( * @param disposeErr 是否处理错误响应,默认-->true */ export function get(url: string, params?: any, disposeErr: boolean = true) { - return httpUtil.get>(url, {params, paramsSerializer}) + return httpUtil.get>(url, {params, paramsSerializer, responseType: 'json'}) .then(({data}) => data) .catch(res => { - if (disposeErr) errHandler(res.response.data) - return Promise.reject(res.response.data) + if (disposeErr) errHandler(res) + return Promise.reject(res) }) } @@ -149,11 +140,11 @@ export function get(url: string, params?: any, disposeErr: boolean = tr * @param disposeErr 是否处理错误响应,默认-->true */ export function post(url: string, body?: any, config?: AxiosConfig, disposeErr: boolean = true) { - return httpUtil.post>(url, body, config) + return httpUtil.post>(url, body, {...config, responseType: 'json'}) .then(({data}) => data) .catch(res => { - if (disposeErr) errHandler(res.response.data) - return Promise.reject(res.response.data) + if (disposeErr) errHandler(res) + return Promise.reject(res) }) } @@ -173,12 +164,12 @@ export function postForm(url: string, body: any, config?: AxiosConfig, dispos 'Content-Type': ContentType.FORM, }, params: config?.params, - responseType: config?.responseType, + responseType: config?.responseType ?? 'json', }) .then(({data}) => data) .catch(res => { - if (disposeErr) errHandler(res.response.data) - return Promise.reject(res.response.data) + if (disposeErr) errHandler(res) + return Promise.reject(res) }) } @@ -198,12 +189,12 @@ export function postMltForm(url: string, body: any, config?: AxiosConfig, dis 'Content-Type': ContentType.MLT_FORM, }, params: config?.params, - responseType: config?.responseType, + responseType: config?.responseType ?? 'json', }) .then(({data}) => data) .catch(res => { - if (disposeErr) errHandler(res.response.data) - return Promise.reject(res.response.data) + if (disposeErr) errHandler(res) + return Promise.reject(res) }) } @@ -258,14 +249,21 @@ export function download(url: string, params?: any, disposeErr: boolean = true) window.URL.revokeObjectURL(blobURL) }) .catch(res => { - if (disposeErr) errHandler(res.response.data) - return Promise.reject(res.response.data) + if (disposeErr) errHandler(res) + return Promise.reject(res) }) } +export function getFileUrl(filename: string) { + const appUserStore = useAppUserStore() + return downloadBaseUrl + filename + '?authorization=' + appUserStore.token +} + export default { get, post, postForm, postMltForm, + download, + getFileUrl, } diff --git a/src/dts/components.d.ts b/src/dts/components.d.ts index 49590c5..55f3c06 100644 --- a/src/dts/components.d.ts +++ b/src/dts/components.d.ts @@ -32,6 +32,7 @@ declare module 'vue' { IxFormItem: typeof import('@idux/components/form')['IxFormItem'] IxFormWrapper: typeof import('@idux/components/form')['IxFormWrapper'] IxIcon: typeof import('@idux/components/icon')['IxIcon'] + IxIconClose: typeof import('@idux/components/icon')['IxIconClose'] IxInput: typeof import('@idux/components/input')['IxInput'] IxInputNumber: typeof import('@idux/components/input-number')['IxInputNumber'] IxLayoutSiderTrigger: typeof import('@idux/components/layout')['IxLayoutSiderTrigger'] @@ -43,12 +44,14 @@ declare module 'vue' { IxModal: typeof import('@idux/components/modal')['IxModal'] IxPagination: typeof import('@idux/components/pagination')['IxPagination'] IxPopconfirm: typeof import('@idux/components/popconfirm')['IxPopconfirm'] + IxPopover: typeof import('@idux/components/popover')['IxPopover'] IxProLayout: typeof import('@idux/pro/layout')['IxProLayout'] IxPwdInput: typeof import('./../components/input/IxPwdInput.vue')['default'] IxRadio: typeof import('@idux/components/radio')['IxRadio'] IxRadioGroup: typeof import('@idux/components/radio')['IxRadioGroup'] IxRow: typeof import('@idux/components/grid')['IxRow'] IxSelect: typeof import('@idux/components/select')['IxSelect'] + IxSelector: typeof import('@idux/components/selector')['IxSelector'] IxSpace: typeof import('@idux/components/space')['IxSpace'] IxTable: typeof import('@idux/components/table')['IxTable'] IxTabs: typeof import('@idux/components/tabs')['IxTabs'] diff --git a/src/pages/dispose-recode/DisposeRecode.vue b/src/pages/dispose-recode/DisposeRecode.vue index 38f986c..1d54f39 100644 --- a/src/pages/dispose-recode/DisposeRecode.vue +++ b/src/pages/dispose-recode/DisposeRecode.vue @@ -114,21 +114,20 @@ const columns: TableColumn[] = [ const pagination = reactive({ pageIndex: 1, - pageSize: 10, + pageSize: 3, total: 100, size: 'sm', showTotal: true, onChange(pageIndex: number, pageSize: number) { - console.log('------', pageIndex, pageSize) pagination.pageIndex = pageIndex pagination.pageSize = pageSize + searchHandler() } }) const tableSpin = ref(false) function searchHandler() { - console.log('------', formGroup.getValue()) tableSpin.value = true DisposeRecodeApi.paging({ ...formGroup.getValue(), @@ -218,7 +217,7 @@ onMounted(() => { .dispose-recode { display: flex; flex-direction: column; - height: 150%; + height: 100%; width: 100%; .title { diff --git a/src/pages/dispose-recode/DisposeRecodeDetail.vue b/src/pages/dispose-recode/DisposeRecodeDetail.vue index 98579fd..b651c2f 100644 --- a/src/pages/dispose-recode/DisposeRecodeDetail.vue +++ b/src/pages/dispose-recode/DisposeRecodeDetail.vue @@ -20,13 +20,9 @@ const data = reactive({ }) onMounted(() => { - console.log(ins) let container = props.container if (container) { container.style.position = 'relative' - for (let child of container.children) { - console.log(child) - } } }) diff --git a/src/pages/frame/ZsyFrame.vue b/src/pages/frame/ZsyFrame.vue index b97bda6..86feffb 100644 --- a/src/pages/frame/ZsyFrame.vue +++ b/src/pages/frame/ZsyFrame.vue @@ -37,10 +37,7 @@ import { type ProLayoutType } from '@idux/pro/layout' import Strings from '@/common/utils/strings.ts' -import { - appName, - downloadBaseUrl -} from '@/common' +import { appName } from '@/common' import { useAppSettingStore } from '@/common/app/app-setting-store.ts' import colls from '@/common/utils/colls.ts' import { @@ -49,10 +46,11 @@ import { } from '@/common/app/contants.ts' import Nav from '@/common/router/nav.ts' import Iconfont from '@/components/iconfont/Iconfont.vue' +import AppApi from '@/common/app/app-api.ts' const appSettingStore = useAppSettingStore() const logoImage = computed(() => { - return Strings.isBlank(appSettingStore.logo) ? defaultLogo : downloadBaseUrl + appSettingStore.logo! + return Strings.isBlank(appSettingStore.logo) ? defaultLogo : AppApi.fileUrl(appSettingStore.logo!) }) const logo = { image: logoImage.value, @@ -131,7 +129,7 @@ function openPageHandler(options: MenuClickOptions) { .page-change-enter-active, .page-change-leave-active { - transition: all .3s ease-in; + transition: opacity .3s ease-in, transform .3s ease-in; } .page-change-enter-from { diff --git a/src/pages/main-zone/header-bar/HeaderBar.vue b/src/pages/main-zone/header-bar/HeaderBar.vue index 257a425..2ceb6f5 100644 --- a/src/pages/main-zone/header-bar/HeaderBar.vue +++ b/src/pages/main-zone/header-bar/HeaderBar.vue @@ -33,11 +33,11 @@ import TabList from '@/pages/main-zone/tab-list/TabList.vue' import Strings from '@/common/utils/strings.ts' import { useAppSettingStore } from '@/common/app/app-setting-store.ts' import UserPanel from '@/pages/main-zone/header-bar/UserPanel.vue' -import { downloadBaseUrl } from '@/common' +import AppApi from '@/common/app/app-api.ts' const appSettingStore = useAppSettingStore() const logo = computed(() => { - return Strings.isBlank(appSettingStore.logo) ? defaultLogo : downloadBaseUrl + appSettingStore.logo! + return Strings.isBlank(appSettingStore.logo) ? defaultLogo : AppApi.fileUrl(appSettingStore.logo!) }) const menuPanel = ref>() diff --git a/src/pages/main-zone/header-bar/UserPanel.vue b/src/pages/main-zone/header-bar/UserPanel.vue index 6a137e1..acb03dc 100644 --- a/src/pages/main-zone/header-bar/UserPanel.vue +++ b/src/pages/main-zone/header-bar/UserPanel.vue @@ -48,10 +48,10 @@ import ModifyPwdForm from '@/pages/main-zone/header-bar/ModifyPwdForm.vue' import UserInfo from '@/pages/main-zone/header-bar/UserInfo.vue' import { VKey } from '@idux/cdk' import Nav from '@/common/router/nav.ts' -import { downloadBaseUrl } from '@/common' import MenuPanelApi from '@/pages/main-zone/menu-panel/menu-panel-api.ts' import Toast from '@/components/toast' import { reloadUserInfo } from '@/common/app' +import AppApi from '@/common/app/app-api.ts' const appSettingStore = useAppSettingStore() const appUserStore = useAppUserStore() @@ -66,7 +66,7 @@ const tabsDtaSource = [ ] const avatar = computed(() => { - return Strings.isBlank(appUserStore.avatar) ? defaultAvatar : downloadBaseUrl + appUserStore.avatar! + return Strings.isBlank(appUserStore.avatar) ? defaultAvatar : AppApi.fileUrl(appUserStore.avatar!) }) const tenantName = computed(() => { return appUserStore.tenantName diff --git a/src/pages/sys/menus/menu-create/MenuCreate.vue b/src/pages/sys/menus/menu-create/MenuCreate.vue index a40eac1..e55aa06 100644 --- a/src/pages/sys/menus/menu-create/MenuCreate.vue +++ b/src/pages/sys/menus/menu-create/MenuCreate.vue @@ -35,7 +35,7 @@ const leftFuns = computed(() => { menuForm.value!.reset() }) .catch(_ => { - Toast.success('添加失败') + Toast.error('添加失败') }) .finally(() => { Toast.close(toastId) diff --git a/src/pages/sys/menus/menu-form/MenuForm.vue b/src/pages/sys/menus/menu-form/MenuForm.vue index f4affb7..3c4a7a2 100644 --- a/src/pages/sys/menus/menu-form/MenuForm.vue +++ b/src/pages/sys/menus/menu-form/MenuForm.vue @@ -58,6 +58,20 @@ + + + + + + + + + + @@ -75,7 +89,61 @@ import { TreeSelectNode } from '@idux/components/tree-select/src/types' import { SelectData } from '@idux/components/select/src/types' import MenuApi from '@/pages/sys/menus/menu-api.ts' import Colls from '@/common/utils/colls.ts' +import icons from '@/components/iconfont/icons.ts' +import { + TableColumn, + TableColumnSelectable +} from '@idux/components/table' +import Iconfont from '@/components/iconfont/Iconfont.vue' +import { TablePagination } from '@idux/components/table/src/types' +interface IconData { + name: string +} + +const selectedRowKeys = ref([]) + +const iconTableDataSource = ref(icons.filter((_, i) => { + return i >= 0 && i < 5 +})) +const selectableColumn = reactive>({ + type: 'selectable', + align: 'center', + multiple: false, + showIndex: false, + trigger: 'click', + onChange: (selectedKeys) => { + menuForm.get('icon')?.setValue(selectedKeys[0] as string) + }, +}) +const columns: TableColumn[] = [ + selectableColumn, + { + title: '图标', + dataKey: 'name', + customCell: ({record}: { record: IconData }) => { + return h(Iconfont, {name: record.name}) + } + }, + { + title: '名称', + dataKey: 'name', + }, +] +const pagination = reactive({ + pageIndex: 1, + pageSize: 5, + total: icons.length, + size: 'sm', + showTotal: true, + onChange(pageIndex: number, pageSize: number) { + pagination.pageIndex = pageIndex + pagination.pageSize = pageSize + iconTableDataSource.value = icons.filter((_, i) => { + return i >= (pageIndex - 1) * pageSize && i < pageIndex * pageSize + }) + } +}) const props = withDefaults(defineProps<{ status?: 'create' | 'view' | 'edit' }>(), { status: 'create', @@ -89,6 +157,7 @@ const menuForm = useFormGroup({ sort: [ 0 ], routeName: [ '' ], sn: [ undefined ], + icon: [ undefined ], }) const menuCategoryCtrl = menuForm.get('menuCategory') const routeNameCtrl = menuForm.get('routeName') @@ -140,6 +209,9 @@ defineExpose({ }, setValue(data: MenuTypes.MenuForm) { menuForm.setValue(data) + if (data.icon != null) { + selectedRowKeys.value = [ data.icon ] + } }, reset() { menuForm.reset() diff --git a/src/pages/sys/menus/menu.d.ts b/src/pages/sys/menus/menu.d.ts index ee6b421..7ebe7d8 100644 --- a/src/pages/sys/menus/menu.d.ts +++ b/src/pages/sys/menus/menu.d.ts @@ -14,7 +14,7 @@ declare global { // 菜单名称 title: string // 图标 - icon: string + icon?: string // 层级; >= 1 tier: number // 排序 @@ -28,7 +28,7 @@ declare global { } type SearchForm = Partial> - type MenuForm = Pick + type MenuForm = Pick type AddForm = Pick type ModifyForm = Pick } diff --git a/src/pages/sys/user/User.vue b/src/pages/sys/user/User.vue index eeb68da..83724b3 100644 --- a/src/pages/sys/user/User.vue +++ b/src/pages/sys/user/User.vue @@ -5,7 +5,7 @@ - +