Merge remote-tracking branch 'origin/main' into main
commit
576a9fa5e6
@ -1,144 +0,0 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"npm.packageManager": "pnpm",
|
||||
"editor.tabSize": 2,
|
||||
"prettier.printWidth": 100, // 超过最大值换行
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"files.eol": "\n",
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/*.log": true,
|
||||
"**/*.log*": true,
|
||||
"**/bower_components": true,
|
||||
"**/dist": true,
|
||||
"**/elehukouben": true,
|
||||
"**/.git": true,
|
||||
"**/.gitignore": true,
|
||||
"**/.svn": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/.idea": true,
|
||||
"**/.vscode": false,
|
||||
"**/yarn.lock": true,
|
||||
"**/tmp": true,
|
||||
"out": true,
|
||||
"dist": true,
|
||||
"node_modules": true,
|
||||
"CHANGELOG.md": true,
|
||||
"examples": true,
|
||||
"res": true,
|
||||
"screenshots": true,
|
||||
"yarn-error.log": true,
|
||||
"**/.yarn": true
|
||||
},
|
||||
"files.exclude": {
|
||||
"**/.cache": true,
|
||||
"**/.editorconfig": true,
|
||||
"**/.eslintcache": true,
|
||||
"**/bower_components": true,
|
||||
"**/.idea": true,
|
||||
"**/tmp": true,
|
||||
"**/.git": true,
|
||||
"**/.svn": true,
|
||||
"**/.hg": true,
|
||||
"**/CVS": true,
|
||||
"**/.DS_Store": true
|
||||
},
|
||||
"files.watcherExclude": {
|
||||
"**/.git/objects/**": true,
|
||||
"**/.git/subtree-cache/**": true,
|
||||
"**/.vscode/**": true,
|
||||
"**/node_modules/**": true,
|
||||
"**/tmp/**": true,
|
||||
"**/bower_components/**": true,
|
||||
"**/dist/**": true,
|
||||
"**/yarn.lock": true
|
||||
},
|
||||
"stylelint.enable": true,
|
||||
"stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"],
|
||||
"path-intellisense.mappings": {
|
||||
"@/": "${workspaceRoot}/src"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||
},
|
||||
"[html]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||
},
|
||||
"[less]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[scss]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "Vue.volar"
|
||||
},
|
||||
"i18n-ally.localesPaths": ["src/locales"],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.namespace": false,
|
||||
"i18n-ally.enabledParsers": ["ts"],
|
||||
"i18n-ally.sourceLanguage": "en",
|
||||
"i18n-ally.displayLanguage": "zh-CN",
|
||||
"i18n-ally.enabledFrameworks": ["vue", "react"],
|
||||
"cSpell.words": [
|
||||
"brotli",
|
||||
"browserslist",
|
||||
"codemirror",
|
||||
"commitlint",
|
||||
"cropperjs",
|
||||
"echart",
|
||||
"echarts",
|
||||
"esnext",
|
||||
"esno",
|
||||
"iconify",
|
||||
"INTLIFY",
|
||||
"lintstagedrc",
|
||||
"logicflow",
|
||||
"nprogress",
|
||||
"pinia",
|
||||
"pnpm",
|
||||
"qrcode",
|
||||
"sider",
|
||||
"sortablejs",
|
||||
"stylelint",
|
||||
"svgs",
|
||||
"unocss",
|
||||
"unplugin",
|
||||
"unref",
|
||||
"videojs",
|
||||
"VITE",
|
||||
"vitejs",
|
||||
"vueuse",
|
||||
"wangeditor",
|
||||
"xingyu",
|
||||
"yudao",
|
||||
"zxcvbn"
|
||||
],
|
||||
// 控制相关文件嵌套展示
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.expand": false,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"*.ts": "$(capture).test.ts, $(capture).test.tsx",
|
||||
"*.tsx": "$(capture).test.ts, $(capture).test.tsx",
|
||||
"*.env": "$(capture).env.*",
|
||||
"package.json": "pnpm-lock.yaml,yarn.lock,LICENSE,README*,CHANGELOG*,CNAME,.gitattributes,.eslintrc-auto-import.json,.gitignore,prettier.config.js,stylelint.config.js,commitlint.config.js,.stylelintignore,.prettierignore,.gitpod.yml,.eslintrc.js,.eslintignore"
|
||||
},
|
||||
"terminal.integrated.scrollback": 10000,
|
||||
"nuxt.isNuxtApp": false
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export interface WarehouseAreaVO {
|
||||
id: number
|
||||
warehouseId: number
|
||||
areaCode: string
|
||||
areaName: string
|
||||
areaSize: number
|
||||
description: string
|
||||
status: number
|
||||
createTime: string
|
||||
}
|
||||
|
||||
export const WarehouseAreaApi = {
|
||||
getWarehouseAreaPage: async (params: any) => {
|
||||
return await request.get({ url: `/erp/warehouse-area/page`, params })
|
||||
},
|
||||
|
||||
getWarehouseArea: async (id: number) => {
|
||||
return await request.get({ url: `/erp/warehouse-area/get?id=` + id })
|
||||
},
|
||||
|
||||
createWarehouseArea: async (data: WarehouseAreaVO) => {
|
||||
return await request.post({ url: `/erp/warehouse-area/create`, data })
|
||||
},
|
||||
|
||||
updateWarehouseArea: async (data: WarehouseAreaVO) => {
|
||||
return await request.put({ url: `/erp/warehouse-area/update`, data })
|
||||
},
|
||||
|
||||
deleteWarehouseArea: async (id: number) => {
|
||||
return await request.delete({ url: `/erp/warehouse-area/delete?id=` + id })
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export interface WarehouseLocationVO {
|
||||
id: number
|
||||
warehouseId: number
|
||||
areaId: number
|
||||
code: string
|
||||
name: string
|
||||
areaSize: number
|
||||
maxLoadWeight: number
|
||||
positionX: number
|
||||
positionY: number
|
||||
positionZ: number
|
||||
allowProductMix: boolean
|
||||
allowBatchMix: boolean
|
||||
status: number
|
||||
createTime: string
|
||||
}
|
||||
|
||||
export const WarehouseLocationApi = {
|
||||
getWarehouseLocationPage: async (params: any) => {
|
||||
return await request.get({ url: `/erp/warehouse-location/page`, params })
|
||||
},
|
||||
|
||||
getWarehouseLocation: async (id: number) => {
|
||||
return await request.get({ url: `/erp/warehouse-location/get?id=` + id })
|
||||
},
|
||||
|
||||
createWarehouseLocation: async (data: WarehouseLocationVO) => {
|
||||
return await request.post({ url: `/erp/warehouse-location/create`, data })
|
||||
},
|
||||
|
||||
updateWarehouseLocation: async (data: WarehouseLocationVO) => {
|
||||
return await request.put({ url: `/erp/warehouse-location/update`, data })
|
||||
},
|
||||
|
||||
deleteWarehouseLocation: async (id: number) => {
|
||||
return await request.delete({ url: `/erp/warehouse-location/delete?id=` + id })
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export interface DeviceOperationOverviewParams {
|
||||
ids?: string
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
timelinePageNo?: number
|
||||
timelinePageSize?: number
|
||||
}
|
||||
|
||||
export interface DeviceOperationOverviewMetricVO {
|
||||
key: string
|
||||
icon: string
|
||||
value: number
|
||||
unit: string
|
||||
change: number
|
||||
}
|
||||
|
||||
export interface DeviceOperationOverviewHourlyStatusVO {
|
||||
hour: string
|
||||
running: number
|
||||
standby: number
|
||||
fault: number
|
||||
offline: number
|
||||
}
|
||||
|
||||
export interface DeviceOperationOverviewSummaryVO {
|
||||
status: 'running' | 'standby' | 'fault' | 'offline'
|
||||
percent: number
|
||||
hours: number
|
||||
}
|
||||
|
||||
export interface DeviceOperationOverviewTimelineSegmentVO {
|
||||
status: 'running' | 'standby' | 'fault' | 'offline'
|
||||
startHour: number
|
||||
endHour: number
|
||||
}
|
||||
|
||||
export interface DeviceOperationOverviewTimelineRowVO {
|
||||
id: string
|
||||
name: string
|
||||
utilizationRate: number
|
||||
segments: DeviceOperationOverviewTimelineSegmentVO[]
|
||||
}
|
||||
|
||||
export interface DeviceOperationOverviewRespVO {
|
||||
metrics: DeviceOperationOverviewMetricVO[]
|
||||
hourlyStatus: DeviceOperationOverviewHourlyStatusVO[]
|
||||
summary: DeviceOperationOverviewSummaryVO[]
|
||||
summaryTotalHours: number
|
||||
timelineRows: DeviceOperationOverviewTimelineRowVO[]
|
||||
totalDevices: number
|
||||
}
|
||||
|
||||
export const DeviceOperationOverviewApi = {
|
||||
getRunOverview: async (params: DeviceOperationOverviewParams) => {
|
||||
return await request.get<DeviceOperationOverviewRespVO>({
|
||||
url: `/iot/device-operation-record/runOverview`,
|
||||
params
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 节假日设置 VO
|
||||
export interface CalHolidayVO {
|
||||
holidayId: number // 流水号
|
||||
theDay: Date // 日期
|
||||
holidayType: string // 日期类型
|
||||
startTime: Date // 开始时间
|
||||
endTime: Date // 结束时间
|
||||
remark: string // 备注
|
||||
attr1: string // 预留字段1
|
||||
attr2: string // 预留字段2
|
||||
attr3: number // 预留字段3
|
||||
attr4: number // 预留字段4
|
||||
createBy: string // 创建者
|
||||
updateBy: string // 更新者
|
||||
}
|
||||
|
||||
// 节假日设置 API
|
||||
export const CalHolidayApi = {
|
||||
// 查询节假日设置分页
|
||||
getCalHolidayPage: async (params: any) => {
|
||||
return await request.get({ url: `/mes/cal-holiday/page`, params })
|
||||
},
|
||||
|
||||
// 查询节假日设置详情
|
||||
getCalHoliday: async (id: number) => {
|
||||
return await request.get({ url: `/mes/cal-holiday/get?id=` + id })
|
||||
},
|
||||
|
||||
// 新增节假日设置
|
||||
createCalHoliday: async (data: CalHolidayVO) => {
|
||||
return await request.post({ url: `/mes/cal-holiday/create`, data })
|
||||
},
|
||||
|
||||
// 修改节假日设置
|
||||
updateCalHoliday: async (data: CalHolidayVO) => {
|
||||
return await request.put({ url: `/mes/cal-holiday/update`, data })
|
||||
},
|
||||
|
||||
// 删除节假日设置
|
||||
deleteCalHoliday: async (id: number) => {
|
||||
return await request.delete({ url: `/mes/cal-holiday/delete?id=` + id })
|
||||
},
|
||||
|
||||
// 导出节假日设置 Excel
|
||||
exportCalHoliday: async (params) => {
|
||||
return await request.download({ url: `/mes/cal-holiday/export-excel`, params })
|
||||
},
|
||||
|
||||
// 删除节假日设置
|
||||
deleteCalHolidayByDay: async (theDay: String) => {
|
||||
return await request.delete({ url: `/mes/cal-holiday/deleteByDay?theDay=` + theDay })
|
||||
},
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export interface CapacityReportVO {
|
||||
id: number
|
||||
deviceCode: string
|
||||
deviceName: string
|
||||
typeName: string
|
||||
deviceStatus: number
|
||||
ratedCapacity: number
|
||||
reportCapacity: number
|
||||
actualCapacity: number
|
||||
workshopName: string
|
||||
}
|
||||
|
||||
export interface CapacityReportQuery {
|
||||
pageNo: number
|
||||
pageSize: number
|
||||
deviceCode?: string
|
||||
deviceName?: string
|
||||
deviceType?: string
|
||||
deviceStatus?: string
|
||||
workshop?: string
|
||||
ids?: string
|
||||
}
|
||||
|
||||
export const DeviceLedgerApi = {
|
||||
/**
|
||||
* 产能报表分页查询
|
||||
*/
|
||||
getCapacityReportPage: async (params: CapacityReportQuery) => {
|
||||
return await request.get({ url: '/mes/device-ledger/capacity-report/page', params })
|
||||
},
|
||||
|
||||
/**
|
||||
* 产能报表导出
|
||||
*/
|
||||
exportCapacityReport: async (params: { ids?: string }) => {
|
||||
return await request.download({ url: '/mes/device-ledger/capacity-report/export-excel', params })
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新设备状态
|
||||
*/
|
||||
updateDeviceLedger: async (data: { id: number; deviceStatus: number }) => {
|
||||
return await request.put({ url: '/mes/device-ledger/update', data })
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export interface DeviceLineVO {
|
||||
id: number
|
||||
code: string
|
||||
isCode?: boolean
|
||||
qrcodeUrl?: string
|
||||
name: string
|
||||
remark: string
|
||||
sort: number
|
||||
parentId: number
|
||||
parentChain: string
|
||||
createTime?: string
|
||||
}
|
||||
|
||||
export interface DeviceLineTreeVO extends DeviceLineVO {
|
||||
children?: DeviceLineTreeVO[]
|
||||
leaf?: boolean
|
||||
}
|
||||
|
||||
export const DeviceLineApi = {
|
||||
getDeviceLine: async (id: number) => {
|
||||
return await request.get({ url: `/mes/device-line/get?id=` + id })
|
||||
},
|
||||
|
||||
getDeviceLineTree: async () => {
|
||||
return await request.get({ url: `/mes/device-line/tree` })
|
||||
},
|
||||
|
||||
regenerateCode: async (id: number, code: string) => {
|
||||
return await request.post({ url: `/mes/device-line/regenerate-code?id=${id}&code=${encodeURIComponent(code)}` })
|
||||
},
|
||||
|
||||
createDeviceLine: async (data: DeviceLineVO) => {
|
||||
return await request.post({ url: `/mes/device-line/create`, data })
|
||||
},
|
||||
|
||||
updateDeviceLine: async (data: DeviceLineVO) => {
|
||||
return await request.put({ url: `/mes/device-line/update`, data })
|
||||
},
|
||||
|
||||
deleteDeviceLine: async (id: number) => {
|
||||
return await request.delete({ url: `/mes/device-line/delete?id=` + id })
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// esop文件表库 VO
|
||||
export interface FileVO {
|
||||
id: number // id
|
||||
configId: number // 配置编号
|
||||
code: string // 文件编码
|
||||
name: string // 文件名
|
||||
path: string // 文件路径
|
||||
url: string // 文件 URL
|
||||
type: string // 文件类型
|
||||
class: string // 文件分类
|
||||
status: number // 文件状态
|
||||
size: number // 文件大小
|
||||
}
|
||||
// 文件预签名地址 Response VO
|
||||
export interface FilePresignedUrlRespVO {
|
||||
// 文件配置编号
|
||||
configId: number
|
||||
// 文件上传 URL
|
||||
uploadUrl: string
|
||||
// 文件 URL
|
||||
url: string
|
||||
}
|
||||
|
||||
export enum ApiUrl{
|
||||
'/admin-api/mes/esop/file/create'
|
||||
}
|
||||
|
||||
// esop文件表库 API
|
||||
export const FileApi = {
|
||||
// 查询esop文件表库分页
|
||||
getFilePage: async (params: any) => {
|
||||
return await request.get({ url: `/esop/file/page`, params })
|
||||
},
|
||||
|
||||
|
||||
// 查询esop文件表库详情
|
||||
getFile: async (id: number) => {
|
||||
return await request.get({ url: `/esop/file/get?id=` + id })
|
||||
},
|
||||
|
||||
// 新增esop文件表库
|
||||
createFile: async (data: FileVO) => {
|
||||
return await request.post({ url: `/esop/file/create`, data })
|
||||
},
|
||||
|
||||
// 修改esop文件表库
|
||||
updateFile: async (data: FileVO) => {
|
||||
return await request.put({ url: `/esop/file/update`, data })
|
||||
},
|
||||
|
||||
// 删除esop文件表库
|
||||
deleteFile: async (id: number) => {
|
||||
return await request.delete({ url: `/esop/file/delete?id=` + id })
|
||||
},
|
||||
|
||||
// 导出esop文件表库 Excel
|
||||
exportFile: async (params) => {
|
||||
return await request.download({ url: `/esop/file/export-excel`, params })
|
||||
},
|
||||
// 获取文件编码
|
||||
generateCode: async () => {
|
||||
return await request.get({ url: `/esop/file/generate`})
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
// 获取文件预签名地址
|
||||
export const getFilePresignedUrl = (path: string) => {
|
||||
return request.get<FilePresignedUrlRespVO>({
|
||||
url: '/infra/file/presigned-url',
|
||||
params: { path }
|
||||
})
|
||||
}
|
||||
|
||||
// 创建文件
|
||||
export const createFile = (data: any) => {
|
||||
return request.post({ url: '/esop/file/create', data })
|
||||
}
|
||||
|
||||
// 上传文件
|
||||
export const updateFile = (data: any) => {
|
||||
return request.upload({ url: '/esop/file/create', data })
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export interface PrintTemplateVO {
|
||||
id: number
|
||||
templateCode: string
|
||||
templateName: string
|
||||
templateType: number
|
||||
templateBizType: number
|
||||
templateJson: string
|
||||
remark: string
|
||||
isEnable: boolean
|
||||
createTime: string
|
||||
}
|
||||
|
||||
export const PrintTemplateApi = {
|
||||
getPrintTemplatePage: async (params: any) => {
|
||||
return await request.get({ url: `/mes/print-template/page`, params })
|
||||
},
|
||||
getPrintTemplate: async (id: number) => {
|
||||
return await request.get({ url: `/mes/print-template/get?id=` + id })
|
||||
},
|
||||
createPrintTemplate: async (data: PrintTemplateVO) => {
|
||||
return await request.post({ url: `/mes/print-template/create`, data })
|
||||
},
|
||||
updatePrintTemplate: async (data: PrintTemplateVO) => {
|
||||
return await request.put({ url: `/mes/print-template/update`, data })
|
||||
},
|
||||
deletePrintTemplate: async (id: number) => {
|
||||
return await request.delete({ url: `/mes/print-template/delete?id=` + id })
|
||||
},
|
||||
exportPrintTemplate: async (params) => {
|
||||
return await request.download({ url: `/mes/print-template/export-excel`, params })
|
||||
},
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 366 B |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@ -1,4 +1,5 @@
|
||||
import Icon from './src/Icon.vue'
|
||||
import IconSelect from './src/IconSelect.vue'
|
||||
import AppIconSelect from './src/AppIconSelect.vue'
|
||||
|
||||
export { Icon, IconSelect }
|
||||
export { Icon, IconSelect, AppIconSelect }
|
||||
|
||||
@ -0,0 +1,280 @@
|
||||
<script lang="ts" setup>
|
||||
import { uniIconsList, uviewIconsList, uniIconsUnicodeMap, uviewIconsUnicodeMap } from './appIconData'
|
||||
|
||||
defineOptions({ name: 'AppIconSelect' })
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
require: false,
|
||||
type: String
|
||||
},
|
||||
clearable: {
|
||||
require: false,
|
||||
type: Boolean
|
||||
}
|
||||
})
|
||||
const emit = defineEmits<{ (e: 'update:modelValue', v: string) }>()
|
||||
|
||||
const visible = ref(false)
|
||||
const inputValue = toRef(props, 'modelValue')
|
||||
const currentActiveType = ref('uni-icons')
|
||||
const filterValue = ref('')
|
||||
|
||||
const iconDataMap: Record<string, string[]> = {
|
||||
'uni-icons': uniIconsList,
|
||||
'uview-plus': uviewIconsList
|
||||
}
|
||||
|
||||
const unicodeMapMap: Record<string, Record<string, string>> = {
|
||||
'uni-icons': uniIconsUnicodeMap,
|
||||
'uview-plus': uviewIconsUnicodeMap
|
||||
}
|
||||
|
||||
const fontFamilyMap: Record<string, string> = {
|
||||
'uni-icons': 'UniIconsFontFamily',
|
||||
'uview-plus': 'uview-iconfont'
|
||||
}
|
||||
|
||||
const tabsList = [
|
||||
{ label: 'uni-icons', name: 'uni-icons' },
|
||||
{ label: 'uview-plus', name: 'uview-plus' }
|
||||
]
|
||||
|
||||
const pageSize = ref(96)
|
||||
const currentPage = ref(1)
|
||||
|
||||
const currentIconList = computed(() => {
|
||||
return iconDataMap[currentActiveType.value] || []
|
||||
})
|
||||
|
||||
const currentUnicodeMap = computed(() => {
|
||||
return unicodeMapMap[currentActiveType.value] || {}
|
||||
})
|
||||
|
||||
const currentFontFamily = computed(() => {
|
||||
return fontFamilyMap[currentActiveType.value] || 'UniIconsFontFamily'
|
||||
})
|
||||
|
||||
const filteredList = computed(() => {
|
||||
return currentIconList.value.filter((v) =>
|
||||
v.toLowerCase().includes(filterValue.value.toLowerCase())
|
||||
)
|
||||
})
|
||||
|
||||
const pageList = computed(() => {
|
||||
if (currentPage.value === 1) {
|
||||
return filteredList.value.slice(0, pageSize.value)
|
||||
}
|
||||
return filteredList.value.slice(
|
||||
pageSize.value * (currentPage.value - 1),
|
||||
pageSize.value * (currentPage.value - 1) + pageSize.value
|
||||
)
|
||||
})
|
||||
|
||||
const iconCount = computed(() => filteredList.value.length)
|
||||
|
||||
function parseIconValue(value: string): { prefix: string; name: string } | null {
|
||||
if (!value || value.indexOf(':') < 0) return null
|
||||
const idx = value.indexOf(':')
|
||||
return {
|
||||
prefix: value.substring(0, idx),
|
||||
name: value.substring(idx + 1)
|
||||
}
|
||||
}
|
||||
|
||||
function getUnicode(name: string): string {
|
||||
return currentUnicodeMap.value[name] || ''
|
||||
}
|
||||
|
||||
function getSelectedFontFamily(fullValue: string): string {
|
||||
const parsed = parseIconValue(fullValue)
|
||||
if (!parsed) return 'UniIconsFontFamily'
|
||||
return fontFamilyMap[parsed.prefix] || 'UniIconsFontFamily'
|
||||
}
|
||||
|
||||
function getSelectedUnicode(fullValue: string): string {
|
||||
const parsed = parseIconValue(fullValue)
|
||||
if (!parsed) return ''
|
||||
const map = unicodeMapMap[parsed.prefix]
|
||||
return map ? map[parsed.name] || '' : ''
|
||||
}
|
||||
|
||||
function handleClick({ props }: any) {
|
||||
currentPage.value = 1
|
||||
currentActiveType.value = props.name
|
||||
}
|
||||
|
||||
function onChangeIcon(item: string) {
|
||||
emit('update:modelValue', currentActiveType.value + ':' + item)
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
function onCurrentChange(page: number) {
|
||||
currentPage.value = page
|
||||
}
|
||||
|
||||
function clearIcon() {
|
||||
emit('update:modelValue', '')
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
if (val) {
|
||||
const parsed = parseIconValue(val)
|
||||
if (parsed && fontFamilyMap[parsed.prefix]) {
|
||||
currentActiveType.value = parsed.prefix
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
watch(
|
||||
() => filterValue.value,
|
||||
() => {
|
||||
currentPage.value = 1
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="selector">
|
||||
<ElInput v-model="inputValue" @click="visible = !visible" :clearable="props.clearable" @clear="clearIcon">
|
||||
<template #append>
|
||||
<ElPopover
|
||||
:visible="visible"
|
||||
:width="355"
|
||||
popper-class="pure-popper"
|
||||
trigger="click"
|
||||
>
|
||||
<template #reference>
|
||||
<div
|
||||
class="h-32px w-40px flex cursor-pointer items-center justify-center"
|
||||
@click="visible = !visible"
|
||||
>
|
||||
<span
|
||||
v-if="inputValue && getSelectedUnicode(inputValue)"
|
||||
class="app-icon-font"
|
||||
:style="{ fontFamily: getSelectedFontFamily(inputValue), fontSize: '18px' }"
|
||||
>{{ getSelectedUnicode(inputValue) }}</span>
|
||||
<span v-else class="text-12px color-gray-500">选择</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<ElInput v-model="filterValue" class="p-2" clearable placeholder="搜索图标" />
|
||||
<ElDivider border-style="dashed" />
|
||||
|
||||
<ElTabs v-model="currentActiveType" @tab-click="handleClick">
|
||||
<ElTabPane
|
||||
v-for="(pane, index) in tabsList"
|
||||
:key="index"
|
||||
:label="pane.label"
|
||||
:name="pane.name"
|
||||
>
|
||||
<ElDivider border-style="dashed" class="tab-divider" />
|
||||
<ElScrollbar height="220px">
|
||||
<ul class="ml-2 flex flex-wrap">
|
||||
<li
|
||||
v-for="(item, key) in pageList"
|
||||
:key="key"
|
||||
:style="inputValue === currentActiveType + ':' + item ? { borderColor: 'var(--el-color-primary)', color: 'var(--el-color-primary)' } : {}"
|
||||
:title="item"
|
||||
class="icon-item mr-2 mt-1 w-1/10 flex cursor-pointer items-center justify-center border border-solid p-2"
|
||||
@click="onChangeIcon(item)"
|
||||
>
|
||||
<span
|
||||
class="app-icon-font"
|
||||
:style="{ fontFamily: currentFontFamily }"
|
||||
>{{ getUnicode(item) }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</ElScrollbar>
|
||||
</ElTabPane>
|
||||
</ElTabs>
|
||||
<ElDivider border-style="dashed" />
|
||||
|
||||
<ElPagination
|
||||
:current-page="currentPage"
|
||||
:page-size="pageSize"
|
||||
:total="iconCount"
|
||||
background
|
||||
class="h-10 flex items-center justify-center"
|
||||
layout="prev, pager, next"
|
||||
size="small"
|
||||
@current-change="onCurrentChange"
|
||||
/>
|
||||
</ElPopover>
|
||||
</template>
|
||||
</ElInput>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@font-face {
|
||||
font-family: 'UniIconsFontFamily';
|
||||
src: url('@/assets/fonts/uniicons.ttf') format('truetype');
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'uview-iconfont';
|
||||
src: url('@/assets/fonts/uview-icons.ttf') format('truetype');
|
||||
font-display: swap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-icon-font {
|
||||
font-style: normal;
|
||||
font-size: 20px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.el-divider--horizontal {
|
||||
margin: 1px auto !important;
|
||||
}
|
||||
|
||||
.tab-divider.el-divider--horizontal {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.icon-item {
|
||||
&:hover {
|
||||
color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
transform: scaleX(1.05);
|
||||
transition: all 0.4s;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-tabs__nav-next) {
|
||||
font-size: 15px;
|
||||
line-height: 32px;
|
||||
box-shadow: -5px 0 5px -6px #ccc;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__nav-prev) {
|
||||
font-size: 15px;
|
||||
line-height: 32px;
|
||||
box-shadow: 5px 0 5px -6px #ccc;
|
||||
}
|
||||
|
||||
:deep(.el-input-group__append) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__item) {
|
||||
height: 30px;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
:deep(.el-tabs__header),
|
||||
:deep(.el-tabs__nav-wrap) {
|
||||
position: static;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,382 @@
|
||||
const uniIconsUnicodeMap: Record<string, string> = {
|
||||
'arrow-down': '\ue6be',
|
||||
'arrow-left': '\ue6bc',
|
||||
'arrow-right': '\ue6bb',
|
||||
'arrow-up': '\ue6bd',
|
||||
'auth': '\ue6ab',
|
||||
'auth-filled': '\ue6cc',
|
||||
'back': '\ue6b9',
|
||||
'bars': '\ue627',
|
||||
'calendar': '\ue6a0',
|
||||
'calendar-filled': '\ue6c0',
|
||||
'camera': '\ue65a',
|
||||
'camera-filled': '\ue658',
|
||||
'cart': '\ue631',
|
||||
'cart-filled': '\ue6d0',
|
||||
'chat': '\ue65d',
|
||||
'chat-filled': '\ue659',
|
||||
'chatboxes': '\ue696',
|
||||
'chatboxes-filled': '\ue692',
|
||||
'chatbubble': '\ue697',
|
||||
'chatbubble-filled': '\ue694',
|
||||
'checkbox': '\ue62b',
|
||||
'checkbox-filled': '\ue62c',
|
||||
'checkmarkempty': '\ue65c',
|
||||
'circle': '\ue65b',
|
||||
'circle-filled': '\ue65e',
|
||||
'clear': '\ue66d',
|
||||
'close': '\ue673',
|
||||
'closeempty': '\ue66c',
|
||||
'cloud-download': '\ue647',
|
||||
'cloud-download-filled': '\ue646',
|
||||
'cloud-upload': '\ue645',
|
||||
'cloud-upload-filled': '\ue648',
|
||||
'color': '\ue6cf',
|
||||
'color-filled': '\ue6c9',
|
||||
'compose': '\ue67f',
|
||||
'contact': '\ue693',
|
||||
'contact-filled': '\ue695',
|
||||
'down': '\ue6b8',
|
||||
'bottom': '\ue6b8',
|
||||
'download': '\ue68d',
|
||||
'download-filled': '\ue681',
|
||||
'email': '\ue69e',
|
||||
'email-filled': '\ue69a',
|
||||
'eye': '\ue651',
|
||||
'eye-filled': '\ue66a',
|
||||
'eye-slash': '\ue6b3',
|
||||
'eye-slash-filled': '\ue6b4',
|
||||
'fire': '\ue6a1',
|
||||
'fire-filled': '\ue6c5',
|
||||
'flag': '\ue65f',
|
||||
'flag-filled': '\ue660',
|
||||
'folder-add': '\ue6a9',
|
||||
'folder-add-filled': '\ue6c8',
|
||||
'font': '\ue6a3',
|
||||
'forward': '\ue6ba',
|
||||
'gear': '\ue664',
|
||||
'gear-filled': '\ue661',
|
||||
'gift': '\ue6a4',
|
||||
'gift-filled': '\ue6c4',
|
||||
'hand-down': '\ue63d',
|
||||
'hand-down-filled': '\ue63c',
|
||||
'hand-up': '\ue63f',
|
||||
'hand-up-filled': '\ue63e',
|
||||
'headphones': '\ue630',
|
||||
'heart': '\ue639',
|
||||
'heart-filled': '\ue641',
|
||||
'help': '\ue679',
|
||||
'help-filled': '\ue674',
|
||||
'home': '\ue662',
|
||||
'home-filled': '\ue663',
|
||||
'image': '\ue670',
|
||||
'image-filled': '\ue678',
|
||||
'images': '\ue650',
|
||||
'images-filled': '\ue64b',
|
||||
'info': '\ue669',
|
||||
'info-filled': '\ue649',
|
||||
'left': '\ue6b7',
|
||||
'link': '\ue6a5',
|
||||
'list': '\ue644',
|
||||
'location': '\ue6ae',
|
||||
'location-filled': '\ue6af',
|
||||
'locked': '\ue66b',
|
||||
'locked-filled': '\ue668',
|
||||
'loop': '\ue633',
|
||||
'mail-open': '\ue643',
|
||||
'mail-open-filled': '\ue63a',
|
||||
'map': '\ue667',
|
||||
'map-filled': '\ue666',
|
||||
'map-pin': '\ue6ad',
|
||||
'map-pin-ellipse': '\ue6ac',
|
||||
'medal': '\ue6a2',
|
||||
'medal-filled': '\ue6c3',
|
||||
'mic': '\ue671',
|
||||
'mic-filled': '\ue677',
|
||||
'micoff': '\ue67e',
|
||||
'micoff-filled': '\ue6b0',
|
||||
'minus': '\ue66f',
|
||||
'minus-filled': '\ue67d',
|
||||
'more': '\ue64d',
|
||||
'more-filled': '\ue64e',
|
||||
'navigate': '\ue66e',
|
||||
'navigate-filled': '\ue67a',
|
||||
'notification': '\ue6a6',
|
||||
'notification-filled': '\ue6c1',
|
||||
'paperclip': '\ue652',
|
||||
'paperplane': '\ue672',
|
||||
'paperplane-filled': '\ue675',
|
||||
'person': '\ue699',
|
||||
'person-filled': '\ue69d',
|
||||
'personadd': '\ue69f',
|
||||
'personadd-filled': '\ue698',
|
||||
'phone': '\ue69c',
|
||||
'phone-filled': '\ue69b',
|
||||
'plus': '\ue676',
|
||||
'plus-filled': '\ue6c7',
|
||||
'plusempty': '\ue67b',
|
||||
'pulldown': '\ue632',
|
||||
'pyq': '\ue682',
|
||||
'qq': '\ue680',
|
||||
'redo': '\ue64a',
|
||||
'redo-filled': '\ue655',
|
||||
'refresh': '\ue657',
|
||||
'refresh-filled': '\ue656',
|
||||
'refreshempty': '\ue6bf',
|
||||
'reload': '\ue6b2',
|
||||
'right': '\ue6b5',
|
||||
'scan': '\ue62a',
|
||||
'search': '\ue654',
|
||||
'settings': '\ue653',
|
||||
'settings-filled': '\ue6ce',
|
||||
'shop': '\ue62f',
|
||||
'shop-filled': '\ue6cd',
|
||||
'smallcircle': '\ue67c',
|
||||
'smallcircle-filled': '\ue665',
|
||||
'sound': '\ue684',
|
||||
'sound-filled': '\ue686',
|
||||
'spinner-cycle': '\ue68a',
|
||||
'staff': '\ue6a7',
|
||||
'staff-filled': '\ue6cb',
|
||||
'star': '\ue688',
|
||||
'star-filled': '\ue68f',
|
||||
'starhalf': '\ue683',
|
||||
'trash': '\ue687',
|
||||
'trash-filled': '\ue685',
|
||||
'tune': '\ue6aa',
|
||||
'tune-filled': '\ue6ca',
|
||||
'undo': '\ue64f',
|
||||
'undo-filled': '\ue64c',
|
||||
'up': '\ue6b6',
|
||||
'top': '\ue6b6',
|
||||
'upload': '\ue690',
|
||||
'upload-filled': '\ue68e',
|
||||
'videocam': '\ue68c',
|
||||
'videocam-filled': '\ue689',
|
||||
'vip': '\ue6a8',
|
||||
'vip-filled': '\ue6c6',
|
||||
'wallet': '\ue6b1',
|
||||
'wallet-filled': '\ue6c2',
|
||||
'weibo': '\ue68b',
|
||||
'weixin': '\ue691'
|
||||
}
|
||||
|
||||
const uviewIconsUnicodeMap: Record<string, string> = {
|
||||
'level': '\ue693',
|
||||
'column-line': '\ue68e',
|
||||
'checkbox-mark': '\ue807',
|
||||
'folder': '\ue7f5',
|
||||
'movie': '\ue7f6',
|
||||
'star-fill': '\ue669',
|
||||
'star': '\ue65f',
|
||||
'phone-fill': '\ue64f',
|
||||
'phone': '\ue622',
|
||||
'apple-fill': '\ue881',
|
||||
'chrome-circle-fill': '\ue885',
|
||||
'backspace': '\ue67b',
|
||||
'attach': '\ue632',
|
||||
'cut': '\ue948',
|
||||
'empty-car': '\ue602',
|
||||
'empty-coupon': '\ue682',
|
||||
'empty-address': '\ue646',
|
||||
'empty-favor': '\ue67c',
|
||||
'empty-permission': '\ue686',
|
||||
'empty-news': '\ue687',
|
||||
'empty-search': '\ue664',
|
||||
'github-circle-fill': '\ue887',
|
||||
'rmb': '\ue608',
|
||||
'person-delete-fill': '\ue66a',
|
||||
'reload': '\ue788',
|
||||
'order': '\ue68f',
|
||||
'server-man': '\ue6bc',
|
||||
'search': '\ue62a',
|
||||
'fingerprint': '\ue955',
|
||||
'more-dot-fill': '\ue630',
|
||||
'scan': '\ue662',
|
||||
'share-square': '\ue60b',
|
||||
'map': '\ue61d',
|
||||
'map-fill': '\ue64e',
|
||||
'tags': '\ue629',
|
||||
'tags-fill': '\ue651',
|
||||
'bookmark-fill': '\ue63b',
|
||||
'bookmark': '\ue60a',
|
||||
'eye': '\ue613',
|
||||
'eye-fill': '\ue641',
|
||||
'mic': '\ue64a',
|
||||
'mic-off': '\ue649',
|
||||
'calendar': '\ue66e',
|
||||
'calendar-fill': '\ue634',
|
||||
'trash': '\ue623',
|
||||
'trash-fill': '\ue658',
|
||||
'play-left': '\ue66d',
|
||||
'play-right': '\ue610',
|
||||
'minus': '\ue618',
|
||||
'plus': '\ue62d',
|
||||
'info': '\ue653',
|
||||
'info-circle': '\ue7d2',
|
||||
'info-circle-fill': '\ue64b',
|
||||
'question': '\ue715',
|
||||
'error': '\ue6d3',
|
||||
'close': '\ue685',
|
||||
'checkmark': '\ue6a8',
|
||||
'android-circle-fill': '\ue67e',
|
||||
'android-fill': '\ue67d',
|
||||
'ie': '\ue87b',
|
||||
'IE-circle-fill': '\ue889',
|
||||
'google': '\ue87a',
|
||||
'google-circle-fill': '\ue88a',
|
||||
'setting-fill': '\ue872',
|
||||
'setting': '\ue61f',
|
||||
'minus-square-fill': '\ue855',
|
||||
'plus-square-fill': '\ue856',
|
||||
'heart': '\ue7df',
|
||||
'heart-fill': '\ue851',
|
||||
'camera': '\ue7d7',
|
||||
'camera-fill': '\ue870',
|
||||
'more-circle': '\ue63e',
|
||||
'more-circle-fill': '\ue645',
|
||||
'chat': '\ue620',
|
||||
'chat-fill': '\ue61e',
|
||||
'bag-fill': '\ue617',
|
||||
'bag': '\ue619',
|
||||
'error-circle-fill': '\ue62c',
|
||||
'error-circle': '\ue624',
|
||||
'close-circle': '\ue63f',
|
||||
'close-circle-fill': '\ue637',
|
||||
'checkmark-circle': '\ue63d',
|
||||
'checkmark-circle-fill': '\ue635',
|
||||
'question-circle-fill': '\ue666',
|
||||
'question-circle': '\ue625',
|
||||
'share': '\ue631',
|
||||
'share-fill': '\ue65e',
|
||||
'shopping-cart': '\ue621',
|
||||
'shopping-cart-fill': '\ue65d',
|
||||
'bell': '\ue609',
|
||||
'bell-fill': '\ue640',
|
||||
'list': '\ue650',
|
||||
'list-dot': '\ue616',
|
||||
'zhihu': '\ue6ba',
|
||||
'zhihu-circle-fill': '\ue709',
|
||||
'zhifubao': '\ue6b9',
|
||||
'zhifubao-circle-fill': '\ue6b8',
|
||||
'weixin-circle-fill': '\ue6b1',
|
||||
'weixin-fill': '\ue6b2',
|
||||
'twitter-circle-fill': '\ue6ab',
|
||||
'twitter': '\ue6aa',
|
||||
'taobao-circle-fill': '\ue6a7',
|
||||
'taobao': '\ue6a6',
|
||||
'weibo-circle-fill': '\ue6a5',
|
||||
'weibo': '\ue6a4',
|
||||
'qq-fill': '\ue6a1',
|
||||
'qq-circle-fill': '\ue6a0',
|
||||
'moments-circel-fill': '\ue69a',
|
||||
'moments': '\ue69b',
|
||||
'qzone': '\ue695',
|
||||
'qzone-circle-fill': '\ue696',
|
||||
'baidu-circle-fill': '\ue680',
|
||||
'baidu': '\ue681',
|
||||
'facebook-circle-fill': '\ue68a',
|
||||
'facebook': '\ue689',
|
||||
'car': '\ue60c',
|
||||
'car-fill': '\ue636',
|
||||
'warning-fill': '\ue64d',
|
||||
'warning': '\ue694',
|
||||
'clock-fill': '\ue638',
|
||||
'clock': '\ue60f',
|
||||
'edit-pen': '\ue612',
|
||||
'edit-pen-fill': '\ue66b',
|
||||
'email': '\ue611',
|
||||
'email-fill': '\ue642',
|
||||
'minus-circle': '\ue61b',
|
||||
'minus-circle-fill': '\ue652',
|
||||
'plus-circle': '\ue62e',
|
||||
'plus-circle-fill': '\ue661',
|
||||
'file-text': '\ue663',
|
||||
'file-text-fill': '\ue665',
|
||||
'pushpin': '\ue7e3',
|
||||
'pushpin-fill': '\ue86e',
|
||||
'grid': '\ue673',
|
||||
'grid-fill': '\ue678',
|
||||
'play-circle': '\ue647',
|
||||
'play-circle-fill': '\ue655',
|
||||
'pause-circle-fill': '\ue654',
|
||||
'pause': '\ue8fa',
|
||||
'pause-circle': '\ue643',
|
||||
'eye-off': '\ue648',
|
||||
'eye-off-outline': '\ue62b',
|
||||
'gift-fill': '\ue65c',
|
||||
'gift': '\ue65b',
|
||||
'rmb-circle-fill': '\ue657',
|
||||
'rmb-circle': '\ue677',
|
||||
'kefu-ermai': '\ue656',
|
||||
'server-fill': '\ue751',
|
||||
'coupon-fill': '\ue8c4',
|
||||
'coupon': '\ue8ae',
|
||||
'integral': '\ue704',
|
||||
'integral-fill': '\ue703',
|
||||
'home-fill': '\ue964',
|
||||
'home': '\ue965',
|
||||
'hourglass-half-fill': '\ue966',
|
||||
'hourglass': '\ue967',
|
||||
'account': '\ue628',
|
||||
'plus-people-fill': '\ue626',
|
||||
'minus-people-fill': '\ue615',
|
||||
'account-fill': '\ue614',
|
||||
'thumb-down-fill': '\ue726',
|
||||
'thumb-down': '\ue727',
|
||||
'thumb-up': '\ue733',
|
||||
'thumb-up-fill': '\ue72f',
|
||||
'lock-fill': '\ue979',
|
||||
'lock-open': '\ue973',
|
||||
'lock-opened-fill': '\ue974',
|
||||
'lock': '\ue97a',
|
||||
'red-packet-fill': '\ue690',
|
||||
'photo-fill': '\ue98b',
|
||||
'photo': '\ue98d',
|
||||
'volume-off-fill': '\ue659',
|
||||
'volume-off': '\ue644',
|
||||
'volume-fill': '\ue670',
|
||||
'volume': '\ue633',
|
||||
'red-packet': '\ue691',
|
||||
'download': '\ue63c',
|
||||
'arrow-up-fill': '\ue6b0',
|
||||
'arrow-down-fill': '\ue600',
|
||||
'play-left-fill': '\ue675',
|
||||
'play-right-fill': '\ue676',
|
||||
'rewind-left-fill': '\ue679',
|
||||
'rewind-right-fill': '\ue67a',
|
||||
'arrow-downward': '\ue604',
|
||||
'arrow-leftward': '\ue601',
|
||||
'arrow-rightward': '\ue603',
|
||||
'arrow-upward': '\ue607',
|
||||
'arrow-down': '\ue60d',
|
||||
'arrow-right': '\ue605',
|
||||
'arrow-left': '\ue60e',
|
||||
'arrow-up': '\ue606',
|
||||
'skip-back-left': '\ue674',
|
||||
'skip-forward-right': '\ue672',
|
||||
'rewind-right': '\ue66f',
|
||||
'rewind-left': '\ue671',
|
||||
'arrow-right-double': '\ue68d',
|
||||
'arrow-left-double': '\ue68c',
|
||||
'wifi-off': '\ue668',
|
||||
'wifi': '\ue667',
|
||||
'empty-data': '\ue62f',
|
||||
'empty-history': '\ue684',
|
||||
'empty-list': '\ue68b',
|
||||
'empty-page': '\ue627',
|
||||
'empty-order': '\ue639',
|
||||
'man': '\ue697',
|
||||
'woman': '\ue69c',
|
||||
'man-add': '\ue61c',
|
||||
'man-add-fill': '\ue64c',
|
||||
'man-delete': '\ue61a',
|
||||
'man-delete-fill': '\ue66a',
|
||||
'zh': '\ue70a',
|
||||
'en': '\ue692'
|
||||
}
|
||||
|
||||
const uniIconsList = Object.keys(uniIconsUnicodeMap)
|
||||
const uviewIconsList = Object.keys(uviewIconsUnicodeMap)
|
||||
|
||||
export { uniIconsList, uviewIconsList, uniIconsUnicodeMap, uviewIconsUnicodeMap }
|
||||
@ -0,0 +1,371 @@
|
||||
<template>
|
||||
<div class="qrcode-action-card">
|
||||
<el-image
|
||||
v-if="imageUrl"
|
||||
:src="imageUrl"
|
||||
:preview-src-list="[imageUrl]"
|
||||
preview-teleported
|
||||
fit="cover"
|
||||
class="qrcode-action-card__img"
|
||||
>
|
||||
<template #error>
|
||||
<div class="qrcode-action-card__error">{{ errorText || emptyText }}</div>
|
||||
</template>
|
||||
</el-image>
|
||||
<div v-else class="qrcode-action-card__error">{{ emptyText }}</div>
|
||||
<div v-if="showActionMask" class="qrcode-action-card__mask">
|
||||
<el-button v-if="showPreview" circle :disabled="!imageUrl" @click="handlePreview">
|
||||
<Icon icon="ep:zoom-in" />
|
||||
</el-button>
|
||||
<el-button v-if="showPrint" circle :disabled="!imageUrl || printDisabled" @click="handlePrint">
|
||||
<Icon icon="ep:printer" />
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="showRefresh"
|
||||
circle
|
||||
:loading="refreshLoading"
|
||||
:disabled="refreshDisabled || refreshLoading"
|
||||
@click="handleRefresh"
|
||||
>
|
||||
<Icon icon="ep:refresh" />
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<HiprintPreviewDialog ref="hiprintPreviewDialogRef" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import request from '@/config/axios'
|
||||
import HiprintPreviewDialog from '@/components/HiprintPreviewDialog/index.vue'
|
||||
import { createImageViewer } from '@/components/ImageViewer'
|
||||
import { ElLoading } from 'element-plus'
|
||||
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
imageUrl?: string
|
||||
printId?: string | number
|
||||
printTitle?: string
|
||||
emptyText?: string
|
||||
refreshUrl?: string
|
||||
refreshMethod?: 'post' | 'put' | 'get'
|
||||
refreshDisabled?: boolean
|
||||
printDisabled?: boolean
|
||||
refreshConfirmText?: string
|
||||
errorText?: string
|
||||
showPreview?: boolean
|
||||
showPrint?: boolean
|
||||
showRefresh?: boolean
|
||||
printAdaptive?: boolean
|
||||
printPaperWidth?: number
|
||||
printPaperHeight?: number
|
||||
printMaxWidth?: number
|
||||
printMaxHeight?: number
|
||||
templateJsonUrl?: string
|
||||
templateJson?: any
|
||||
printData?: Record<string, any>
|
||||
}>(),
|
||||
{
|
||||
imageUrl: '',
|
||||
printId: '',
|
||||
printTitle: '二维码打印预览',
|
||||
emptyText: '',
|
||||
refreshUrl: '',
|
||||
refreshMethod: 'post',
|
||||
refreshDisabled: false,
|
||||
printDisabled: false,
|
||||
refreshConfirmText: '',
|
||||
errorText: '',
|
||||
showPreview: true,
|
||||
showPrint: true,
|
||||
showRefresh: true,
|
||||
printAdaptive: true,
|
||||
printPaperWidth: 80,
|
||||
printPaperHeight: 80,
|
||||
printMaxWidth: 180,
|
||||
printMaxHeight: 120,
|
||||
templateJsonUrl: '',
|
||||
templateJson: undefined,
|
||||
printData: () => ({})
|
||||
}
|
||||
)
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'refresh-success', data: any): void
|
||||
}>()
|
||||
|
||||
const hiprintPreviewDialogRef = ref()
|
||||
const refreshLoading = ref(false)
|
||||
|
||||
const showActionMask = computed(() => props.showPreview || props.showPrint || props.showRefresh)
|
||||
|
||||
const buildQrcodeTemplateJson = (
|
||||
qrcodeUrl: string,
|
||||
printId: string,
|
||||
imageWidth: number,
|
||||
imageHeight: number,
|
||||
paperHeight: number
|
||||
) => ({
|
||||
panels: [
|
||||
{
|
||||
index: 0,
|
||||
name: 1,
|
||||
width: imageWidth,
|
||||
height: paperHeight,
|
||||
paperHeader: 0,
|
||||
paperFooter: 0,
|
||||
printElements: [
|
||||
{
|
||||
options: {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
src: qrcodeUrl,
|
||||
field: 'qrcodeUrl',
|
||||
title: '二维码图片',
|
||||
testData: qrcodeUrl
|
||||
},
|
||||
printElementType: {
|
||||
title: '图片',
|
||||
type: 'image'
|
||||
}
|
||||
},
|
||||
{
|
||||
options: {
|
||||
left: 0,
|
||||
top: imageHeight + 2,
|
||||
height: 10,
|
||||
width: imageWidth,
|
||||
testData: printId,
|
||||
title: 'ID',
|
||||
field: 'printId',
|
||||
textAlign: 'center',
|
||||
fontSize: 10
|
||||
},
|
||||
printElementType: {
|
||||
title: '文本',
|
||||
type: 'text'
|
||||
}
|
||||
}
|
||||
],
|
||||
paperNumberDisabled: true,
|
||||
paperNumberContinue: true,
|
||||
watermarkOptions: {},
|
||||
panelLayoutOptions: {}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const getImageSize = (src: string) =>
|
||||
new Promise<{ width: number; height: number }>((resolve, reject) => {
|
||||
const img = new Image()
|
||||
img.onload = () => {
|
||||
const width = img.naturalWidth || img.width
|
||||
const height = img.naturalHeight || img.height
|
||||
if (!width || !height) {
|
||||
reject(new Error('invalid image size'))
|
||||
return
|
||||
}
|
||||
resolve({ width, height })
|
||||
}
|
||||
img.onerror = () => reject(new Error('image load failed'))
|
||||
img.src = src
|
||||
})
|
||||
|
||||
const resolvePrintSize = async (src: string) => {
|
||||
const fallbackWidth = Math.max(20, Number(props.printPaperWidth) || 80)
|
||||
const fallbackHeight = Math.max(20, Number(props.printPaperHeight) || 80)
|
||||
if (!props.printAdaptive) {
|
||||
return { width: fallbackWidth, height: fallbackHeight }
|
||||
}
|
||||
try {
|
||||
const size = await getImageSize(src)
|
||||
const ratio = size.width / size.height
|
||||
if (!Number.isFinite(ratio) || ratio <= 0) {
|
||||
return { width: fallbackWidth, height: fallbackHeight }
|
||||
}
|
||||
if (ratio >= 1) {
|
||||
const height = fallbackHeight
|
||||
const width = Math.min(Math.max(fallbackWidth, Math.round(height * ratio)), Number(props.printMaxWidth) || 180)
|
||||
return { width, height }
|
||||
}
|
||||
const width = fallbackWidth
|
||||
const height = Math.min(Math.max(fallbackHeight, Math.round(width / ratio)), Number(props.printMaxHeight) || 120)
|
||||
return { width, height }
|
||||
} catch {
|
||||
return { width: fallbackWidth, height: fallbackHeight }
|
||||
}
|
||||
}
|
||||
|
||||
const handlePreview = () => {
|
||||
if (!props.imageUrl) return
|
||||
createImageViewer({
|
||||
zIndex: 9999999,
|
||||
urlList: [props.imageUrl]
|
||||
})
|
||||
}
|
||||
|
||||
const replaceTemplateValues = (templateJson: any, printData: Record<string, any>) => {
|
||||
if (!templateJson?.panels) return templateJson
|
||||
|
||||
return {
|
||||
...templateJson,
|
||||
panels: templateJson.panels.map((panel: any) => ({
|
||||
...panel,
|
||||
printElements: panel.printElements?.map((element: any) => {
|
||||
if (!element?.options?.qid) return element
|
||||
|
||||
const qid = element.options.qid
|
||||
const value = printData[qid]
|
||||
|
||||
if (value === undefined || value === null) return element
|
||||
|
||||
const newOptions = { ...element.options }
|
||||
|
||||
if (qid === 'qrcodeUrl') {
|
||||
newOptions.src = value
|
||||
newOptions.testData = value
|
||||
} else {
|
||||
newOptions.field = qid
|
||||
if (newOptions.testData === undefined || newOptions.testData === '') {
|
||||
newOptions.testData = String(value)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...element,
|
||||
options: newOptions
|
||||
}
|
||||
}) || []
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
const handlePrint = async () => {
|
||||
if (!props.imageUrl || !props.showPrint) return
|
||||
|
||||
let templateJson: any
|
||||
let printData: Record<string, any>
|
||||
|
||||
printData = {
|
||||
qrcodeUrl: props.imageUrl,
|
||||
printId: props.printId === undefined || props.printId === null ? '' : String(props.printId),
|
||||
...props.printData
|
||||
}
|
||||
|
||||
if (props.templateJson) {
|
||||
templateJson = replaceTemplateValues(props.templateJson, printData)
|
||||
} else if (props.templateJsonUrl) {
|
||||
try {
|
||||
const response = await request.get({ url: props.templateJsonUrl })
|
||||
templateJson = typeof response.data === 'string' ? JSON.parse(response.data) : response.data
|
||||
templateJson = replaceTemplateValues(templateJson, printData)
|
||||
} catch (error) {
|
||||
console.error('获取打印模板失败', error)
|
||||
message.error('获取打印模板失败')
|
||||
return
|
||||
}
|
||||
} else {
|
||||
const printSize = await resolvePrintSize(props.imageUrl)
|
||||
const imageWidth = printSize.width
|
||||
const imageHeight = printSize.height
|
||||
const printId = props.printId === undefined || props.printId === null ? '' : String(props.printId)
|
||||
const idAreaHeight = printId ? 14 : 0
|
||||
const paperHeight = imageHeight + idAreaHeight
|
||||
|
||||
templateJson = buildQrcodeTemplateJson(props.imageUrl, printId, imageWidth, imageHeight, paperHeight)
|
||||
}
|
||||
|
||||
const paperSize = templateJson?.panels?.[0] ? {
|
||||
width: templateJson.panels[0].width,
|
||||
height: templateJson.panels[0].height
|
||||
} : { width: 80, height: 80 }
|
||||
|
||||
hiprintPreviewDialogRef.value?.open({
|
||||
title: props.printTitle,
|
||||
printData,
|
||||
templateJson,
|
||||
withDefaultQrcodeLayout: false,
|
||||
paperSize
|
||||
})
|
||||
}
|
||||
|
||||
const handleRefresh = async () => {
|
||||
if (!props.refreshUrl || props.refreshDisabled || !props.showRefresh) return
|
||||
try {
|
||||
await message.confirm(props.refreshConfirmText || '确认刷新二维码吗?')
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
refreshLoading.value = true
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: t('common.loading'),
|
||||
background: 'rgba(0, 0, 0, 0.3)'
|
||||
})
|
||||
try {
|
||||
let data
|
||||
if (props.refreshMethod === 'get') {
|
||||
data = await request.get({ url: props.refreshUrl })
|
||||
} else if (props.refreshMethod === 'put') {
|
||||
data = await request.put({ url: props.refreshUrl })
|
||||
} else {
|
||||
data = await request.post({ url: props.refreshUrl })
|
||||
}
|
||||
emit('refresh-success', data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
} finally {
|
||||
loading.close()
|
||||
refreshLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.qrcode-action-card {
|
||||
position: relative;
|
||||
height: 150px;
|
||||
width: fit-content;
|
||||
min-width: 150px;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
background: var(--el-fill-color-blank);
|
||||
}
|
||||
|
||||
.qrcode-action-card__img {
|
||||
height: 150px;
|
||||
width: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.qrcode-action-card__error {
|
||||
height: 150px;
|
||||
min-width: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.qrcode-action-card__mask {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.qrcode-action-card:hover .qrcode-action-card__mask {
|
||||
opacity: 1;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue