Compare commits

..

No commits in common. "main" and "addwork" have entirely different histories.

308 changed files with 7579 additions and 65611 deletions

View File

@ -1,5 +1,5 @@
# 页面标题
VITE_APP_TITLE = 实验室信息管理系统
VITE_APP_TITLE = 实验室管理系统
# 开发环境配置
VITE_APP_ENV = 'development'

View File

@ -1,5 +1,5 @@
# 页面标题
VITE_APP_TITLE = 实验室信息管理系统
VITE_APP_TITLE = 实验室管理系统
# 生产环境配置
VITE_APP_ENV = 'production'

View File

@ -1,5 +1,5 @@
# 页面标题
VITE_APP_TITLE = 实验室信息管理系统
VITE_APP_TITLE = 实验室管理系统
# 生产环境配置
VITE_APP_ENV = 'staging'

1
.gitignore vendored
View File

@ -21,4 +21,3 @@ selenium-debug.log
package-lock.json
yarn.lock
dist.zip

View File

@ -2,80 +2,29 @@ import js from "@eslint/js";
import globals from "globals";
import pluginVue from "eslint-plugin-vue";
import { defineConfig } from "eslint/config";
// 引入 TypeScript ESLint 插件和解析器
import tsParser from "@typescript-eslint/parser";
import tsPlugin from "@typescript-eslint/eslint-plugin";
export default defineConfig([
// 1. 基础 JS 规则(适用于 JS/Vue 文件)
{
files: ["**/*.{js,mjs,cjs,vue}"],
plugins: { js },
extends: ["js/recommended"]
},
// 2. 浏览器环境全局变量(适用于 JS/Vue/TS 文件)
{
files: ["**/*.{js,mjs,cjs,vue,ts,tsx}"],
languageOptions: {
globals: globals.browser
}
},
// 3. Vue 基础规则
{ files: ["**/*.{js,mjs,cjs,vue}"], plugins: { js }, extends: ["js/recommended"] },
{ files: ["**/*.{js,mjs,cjs,vue}"], languageOptions: { globals: globals.browser } },
pluginVue.configs["flat/essential"],
// 4. Auto-import 插件配置(保持不变)
{
plugins: {
'auto-import': {
extensions: ['.js', '.mjs', '.jsx', '.vue', '.ts', '.tsx'], // 增加 TS 扩展名
extensions: ['.js', '.mjs', '.jsx', '.vue'],
},
},
},
// 5. TypeScript 专属配置(核心)
{
// 仅对 TS/TSX 文件生效
files: ["**/*.{ts,tsx}"],
// 指定 TS 解析器
languageOptions: {
parser: tsParser,
// 解析器选项(需与 tsconfig.json 匹配)
parserOptions: {
ecmaVersion: "latest", // 支持最新 ES 语法
sourceType: "module", // 模块类型(ESM)
project: "./tsconfig.json", // 指向项目的 tsconfig.json(启用类型检查)
tsconfigRootDir: process.cwd(), // 确保找到 tsconfig.json 的根目录
},
},
// 引入 TS 插件
plugins: {
"@typescript-eslint": tsPlugin,
},
// 继承 TS 推荐规则(可选,按需调整)
extends: [
"plugin:@typescript-eslint/recommended", // 基础推荐规则
"plugin:@typescript-eslint/recommended-requiring-type-checking" // 依赖类型检查的规则(需 project 配置)
],
// TS 专属规则(可自定义)
rules: {
// 示例:禁用某些严格规则(根据需求调整)
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-explicit-any": "warn"
}
},
// 6. 全局规则(覆盖所有文件)
{
rules: {
// 可在此处添加适用于所有文件的规则
// 例如关闭不必要的规则
"no-undef": "off" // TS 已有类型检查,可关闭 JS 的 undef 检查
'auto-import/export': 'error',
},
},
// 7. Auto-import 配置(增加 TS 支持)
{
settings: {
'auto-import': {
imports: ['vue', 'vue-router'], // 自动导入的模块
// 增加 TS 扩展名
extensions: ['.js', '.mjs', '.jsx', '.vue', '.ts', '.tsx'],
// 自动导入的模块配置,与 unplugin-auto-import 配置一致
imports: ['vue'],
},
},
},

View File

@ -7,7 +7,7 @@
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="/favicon.ico">
<title>实验室信息管理系统</title>
<title>实验室检验系统</title>
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
<style>

View File

@ -1,27 +1,24 @@
{
"name": "czlis",
"version": "8.0.0",
"description": "实验室信息管理系统",
"description": "实验室管理系统",
"author": "工具",
"license": "MIT",
"type": "module",
"scripts": {
"dev": "vite",
"dev:prod": "vite",
"build:prod": "vite build",
"build:stage": "vite build --mode staging",
"preview": "vite preview"
},
"dependencies": {
"@element-plus/icons-vue": "2.3.1",
"@fingerprintjs/fingerprintjs": "^4.6.2",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "10.6.1",
"axios": "0.27.2",
"clipboard": "^2.0.11",
"dayjs": "^1.11.18",
"echarts": "5.4.3",
"element-plus": "^2.11.2",
"element-plus": "2.4.3",
"file-saver": "2.0.5",
"fuse.js": "6.6.2",
"jquery": "^3.7.1",
@ -29,48 +26,30 @@
"js-cookie": "3.0.5",
"jsbarcode": "^3.12.1",
"jsencrypt": "3.3.2",
"lodash": "^4.17.21",
"mitt": "^3.0.1",
"nprogress": "0.2.0",
"pinia": "2.1.7",
"pinia-plugin-persistedstate": "^4.0.0",
"pinyin-pro": "^3.26.0",
"select2": "^4.1.0-rc.0",
"socket.io-client": "^4.8.1",
"sortablejs": "^1.14.0",
"three": "^0.181.2",
"vue": "3.5.23",
"vue": "3.4.0",
"vue-cropper": "1.1.1",
"vue-plugin-hiprint": "^0.0.60",
"vue-router": "4.2.5",
"vue3-print-nb": "^0.1.4",
"vue3-seamless-scroll": "^3.0.2",
"vue3-select2-component": "^0.1.7",
"vuedraggable": "^4.1.0",
"vxe-pc-ui": "^4.3.2",
"vxe-table": "^4.9.4"
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@eslint/js": "^9.27.0",
"@types/lodash": "^4.17.21",
"@types/vue": "^2.0.0",
"@typescript-eslint/eslint-plugin": "^8.46.2",
"@typescript-eslint/parser": "^8.46.2",
"@vitejs/plugin-vue": "5.0.1",
"@vue/compiler-sfc": "3.4.0",
"eslint": "^9.27.0",
"eslint-plugin-auto-import": "^0.1.1",
"eslint-plugin-vue": "^10.1.0",
"globals": "^16.1.0",
"rollup-plugin-visualizer": "^6.0.5",
"sass": "1.69.5",
"terser": "^5.46.0",
"typescript": "^5.9.3",
"unplugin-auto-import": "0.17.1",
"unplugin-vue-setup-extend-plus": "1.0.0",
"vite": "5.0.4",
"vite-plugin-compression": "0.5.1",
"vite-plugin-imagemin": "^0.6.1",
"vite-plugin-svg-icons": "2.0.1"
}
}

4492
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -5,18 +5,6 @@
<script setup>
import useSettingsStore from '@/store/modules/settings'
import { handleThemeStyle } from '@/utils/theme'
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
import { useFingerprint } from '@/utils/useFingerprint';
import { useCommonStore } from '@/store/modules/commonStore'
const mbStore = useCommonStore();
// 使用自定义hook获取指纹信息
const { fingerprint, getFingerprint } = useFingerprint();
if (!mbStore.fingerprint) {
// 重新获取指纹
getFingerprint();
mbStore.setFingerprint(fingerprint)
}
onMounted(() => {
nextTick(() => {
@ -24,8 +12,4 @@ onMounted(() => {
handleThemeStyle(useSettingsStore().theme)
})
})
</script>
<style lang="scss" scoped></style>

View File

@ -1,34 +0,0 @@
import request from '@/utils/request';
// 查询数据列表
export function applyQueryList(query?: Object) {
return request({
url: '/cancelCheck/queryList',
method: 'get',
params: query,
});
}
// 获取明细数据
export function queryXmDetail(query?: Object) {
return request({
url: '/cancelCheck/queryDetail',
method: 'get',
params: query,
});
}
// 退检
export function confirmRefund(query?: Object) {
return request({
url: '/cancelCheck/confirm',
method: 'get',
params: query,
});
}
// 拒绝退费
export function refuseRefund(query?: Object) {
return request({
url: '/cancelCheck/refuse',
method: 'get',
params: query,
});
}

View File

@ -1,51 +0,0 @@
//@ts-ignore js语法检查忽略
import request from '@/utils/request';
// 获取模版数据
export function getInputMdl(query?: Object) {
return request({
url: '/batchIn/getInputMdl',
method: 'get',
params: query,
});
}
// 获取模版明细数据
export function getInputMdlDetail(query?: Object) {
return request({
url: '/batchIn/getInputMdlDetail',
method: 'get',
params: query,
});
}
// 确认模版数据
export function confirmInputMdl(query?: Object) {
return request({
url: '/batchIn/confirmInputMdl',
method: 'post',
data: query,
});
}
// 调整数据
export function adjustData(query?: Object) {
return request({
url: '/batchCheck/adjustData',
method: 'post',
data: query,
});
}
// 读取数据
export function readData(query?: Object) {
return request({
url: '/batchCheck/readData',
method: 'post',
data: query,
});
}
// 保存数据
export function saveData(query?: Object) {
return request({
url: '/batchCheck/saveData',
method: 'post',
data: query,
});
}

View File

@ -1,140 +0,0 @@
//@ts-ignore js语法检查忽略
import request from '@/utils/request';
// 生成条码
export function addObj(query?: Object) {
return request({
url: 'zyreq/add',
method: 'get',
params: query,
});
}
// 查询申请单列表
export function fetchCodeList(query?: Object) {
return request({
url: '/zyreq/query',
method: 'get',
params: query,
});
}
// 查询项目
export function fetchCodeDetail(query?: Object) {
return request({
url: 'zyreq/detail',
method: 'get',
params: query,
});
}
// 执行条码
export function fetchCodeExec(query?: Object) {
return request({
url: '/zyreq/exec',
method: 'get',
params: query,
});
}
// 执行条码
export function printSendSample(query?: Object) {
return request({
url: '/zyreq/printSendSample',
method: 'post',
data: query,
});
}
// 获取申请单主表信息
export function fetchCodeReqmain(query?: Object) {
return request({
url: 'zyreq/reqmain',
method: 'get',
params: query,
});
}
// 查询报告单列表
export function reportList(query?: Object) {
return request({
url: '/report/ReportList',
method: 'get',
params: query,
});
}
// 查询报告Pdf预览
export function reportViewPdf(query?: Object) {
return request({
url: '/report/pdf',
method: 'get',
params: query,
});
}
// 查询报告Pdf打印
export function reportPrintPdf(query?: Object) {
return request({
url: '/report/print',
method: 'post',
data: query,
});
}
// 查询项目报告
export function reportResult(query?: Object) {
return request({
url: '/report/result',
method: 'post',
data: query,
});
}
// 查询微生物报告
export function reportMedList(query?: Object) {
return request({
url: '/report/MedList',
method: 'post',
data: query,
});
}
// 查询微生物报告
export function reportFsresult(query?: Object) {
return request({
url: '/report/fsresult',
method: 'post',
data: query,
});
}
// 打印机插件检查
export function printcheck(query?: Object) {
return request({
url: '/zyreq/printcheck',
method: 'get',
params: query,
});
}
// 打印机插件设置
export function printsetup(query?: Object) {
return request({
url: '/zyreq/printsetup',
method: 'get',
params: query,
});
}
// 取消采样
export function cancelExecSQD(query?: Object) {
return request({
url: '/zyreq/cancelExecSQD',
method: 'post',
data: query,
});
}
export function getSQDDetailList(query?: Object) {
return request({
url: '/report/getSQDDetail',
method: 'get',
params: query,
});
}
export function getCdssUrl(query?: Object) {
return request({
url: '/report/InterfaceData',
method: 'post',
data: query,
});
}

View File

@ -1,30 +0,0 @@
//@ts-ignore js语法检查忽略
import request from '@/utils/request';
//获取报告列表
export function queryList(query?: Object) {
return request({
url: '/labCheckrules/queryList',
method: 'get',
params: query,
});
}
//保存报告列表
export function updateRules(data: Object) {
return request({
url: '/labCheckrules/update',
method: 'post',
data: data,
});
}
//删除报告列表
export function deleteRules(data: Object) {
return request({
url: '/labCheckrules/delete',
method: 'get',
params: data,
});
}

View File

@ -1,56 +0,0 @@
// @ts-ignore
import request from "@/utils/request";
//获取批量审核列表数据
export function queryBatchCheckListService(query: any) {
return request({
url: "/batchCheck/query",
method: "post",
data: query,
});
}
//批量审核
export function batchCheckService(data: any) {
return request({
url: "/batchCheck/checkData",
method: "post",
data,
});
}
//取消批量审核
export function cancelBatchCheckService(data: any) {
return request({
url: "/batchCheck/unCheckData",
method: "post",
data,
});
}
//获取检验结果明细数据
export function queryBatchCheckDetailService(query: any) {
return request({
url: "/batchCheck/queryDetailResult",
method: "get",
params: query,
});
}
// 批量打印查询
export function queryBatchPrintService(query: any) {
return request({
url: "/batchPrint/queryList",
method: "post",
data: query,
});
}
// 批量打印数据
export function batchPrintAll(query: any) {
return request({
url: "/batchPrint/printAll",
method: "post",
data: query,
});
}

View File

@ -0,0 +1,38 @@
import request from '@/utils/request'
// 查询字典数据列表
export function queryComDictListService(query) {
return request({
url: '/comdict/query',
method: 'get',
params: query
})
}
//增加字典数据
export function addComDictService(data) {
return request({
url: '/comdict/add',
method: 'post',
data
})
}
//修改字典数据
export function updateComDictService(data) {
return request({
url: '/comdict/update',
method: 'post',
data
})
}
// 根据字典类型查询字典数据信息
export function getComDicts(zdlb) {
return request({
url: '/comdict/getComDicts',
method: 'get',
params: zdlb
})
}

View File

@ -1,47 +0,0 @@
// @ts-ignore
import request from '@/utils/request'
// 查询字典数据列表
export function queryComDictListService(query?: Object) {
return request({
url: '/comdict/query',
method: 'get',
params: query
})
}
//增加字典数据
export function addComDictService(data?: Object) {
return request({
url: '/comdict/add',
method: 'post',
data
})
}
//修改字典数据
export function updateComDictService(data?: Object) {
return request({
url: '/comdict/update',
method: 'post',
data
})
}
//删除字典数据
export function delComDictService(data?: Object) {
return request({
url: '/comdict/del',
method: 'delete',
data
})
}
// 根据字典类型查询字典数据信息
export function getComDicts(zdlb?: Object) {
return request({
url: '/comdict/getComDicts',
method: 'get',
params: zdlb
})
}

View File

@ -0,0 +1,18 @@
import request from '@/utils/request'
// 查询字典数据列表
export function querylabInstrList(query) {
return request({
url: '/instr/query',
method: 'get',
params: query
})
}
// 查询字典数据列表
export function querylabInstrListByLisgroup(query) {
return request({
url: '/instr/querybylisgroup',
method: 'get',
params: query
})
}

View File

@ -1,19 +0,0 @@
// @ts-ignore
import request from '@/utils/request'
// 查询字典数据列表
export function querylabInstrList(query?: Object) {
return request({
url: '/instr/query',
method: 'get',
params: query
})
}
// 查询字典数据列表
export function querylabInstrListByLisgroup(query?: Object) {
return request({
url: '/instr/querybylisgroup',
method: 'get',
params: query
})
}

View File

@ -1,30 +0,0 @@
// 抗菌药物字典 xm_med
// @ts-ignore
import request from '@/utils/request'
// 查询药物列表(后端全量返回,不分页)
export function listMed(params) {
return request({
url: '/xmMed/query',
method: 'get',
params
})
}
// 新增/修改药物(后端按 ywdh 存在即更新),入参为数组
export function addMed(data) {
return request({
url: '/xmMed/add',
method: 'post',
data
})
}
// 删除药物
export function delMed(ywdh) {
return request({
url: '/xmMed/del',
method: 'get',
params: { ywdh }
})
}

View File

@ -1,39 +0,0 @@
// 常用抗生素组 xm_medgroup
// @ts-ignore
import request from '@/utils/request'
// 查询(不传条件返回全部明细,前端去重得到组名)
export function listMedgroup(params) {
return request({
url: '/xmMedgroup/query',
method: 'get',
params
})
}
// 新增明细(无 @RequestBody,参数走 query)
export function addMedgroup(params) {
return request({
url: '/xmMedgroup/add',
method: 'post',
params
})
}
// 修改明细(无 @RequestBody,参数走 query)
export function updateMedgroup(params) {
return request({
url: '/xmMedgroup/update',
method: 'post',
params
})
}
// 删除明细
export function delMedgroup(params) {
return request({
url: '/xmMedgroup/del',
method: 'get',
params
})
}

View File

@ -1,39 +0,0 @@
// 抗生素通道号 xm_medinter
// @ts-ignore
import request from '@/utils/request'
// 查询某仪器的通道号列表
export function listMedinter(params) {
return request({
url: '/xmMedinter/query',
method: 'get',
params
})
}
// 新增通道号
export function addMedinter(data) {
return request({
url: '/xmMedinter/add',
method: 'post',
data
})
}
// 修改通道号(只改药物)
export function updateMedinter(data) {
return request({
url: '/xmMedinter/update',
method: 'post',
data
})
}
// 删除通道号
export function delMedinter(params) {
return request({
url: '/xmMedinter/del',
method: 'get',
params
})
}

View File

@ -1,148 +0,0 @@
// @ts-ignore
import request from '@/utils/request'
//检验项目界面
//获取列表数据
export function queryXmInfoNewService(query: any) {
return request({
url: '/xminfo/query',
method: 'get',
params: query
})
}
//获取明细数据
export function queryXmInfoNewDetailService(xmid: any, yq: any, xmdh: any) {
return request({
url: '/xminfo/queryDetail',
method: 'get',
params: {
xmid,
yq,
xmdh
}
})
}
//获取项目基本信息
export function queryXmInfoNew(xmgl: any) {
return request({
url: '/xminfo/queryXmInfoNew',
method: 'get',
params: {
xmgl
}
})
}
//获取危急值特定判断条件数据
export function queryXmAlarmdetailNew(xmid: number) {
return request({
url: '/xminfo/queryXmAlarmdetailNew',
method: 'get',
params: { xmid }
})
}
//新增危急值特定判断条件数据
export function addXmAlarmdetailNew(query: any) {
return request({
url: '/xminfo/addXmAlarmdetailNew',
method: 'post',
data: query
})
}
//新增危急值特定判断条件数据
export function delXmAlarmdetailNew(xmid: number, seq: number) {
return request({
url: '/xminfo/delXmAlarmdetailNew',
method: 'get',
params: {
xmid,
seq
}
})
}
//保存数据
export function saveDataService(query: any) {
return request({
url: '/xminfo/update',
method: 'post',
data: query
})
}
//删除常用取值
export function delXmValNewService(query: any) {
return request({
url: '/xminfo/delXmValNew',
method: 'get',
params: query
})
}
//删除常见描述
export function delXmTextresultNew(query: any) {
return request({
url: '/xminfo/delXmTextresultNew',
method: 'get',
params: query
})
}
//删除仪器对照
export function deleteXmInfoVs(query: any) {
return request({
url: '/xminfo/delvs',
method: 'delete',
params: query
})
}
//删除检验项目
export function deleteXmInfoService(query: any) {
return request({
url: '/xminfo/del',
method: 'delete',
params: query
})
}
//删除参考值明细
export function delXmRefNew(query: any) {
return request({
url: '/xminfo/delXmRefNew',
method: 'get',
params: query
})
}
//查询批量数据
export function queryBatchList(query?: Object) {
return request({
url: '/xminfo/queryBatch',
method: 'get',
params: query
})
}
//批量新增数据
export function addBatch(query?: Object) {
return request({
url: '/xminfo/addBatch',
method: 'post',
data: query
})
}
//批量删除数据
export function delBatch(query?: Object) {
return request({
url: '/xminfo/delBatch',
method: 'post',
data: query
})
}

View File

@ -1,206 +0,0 @@
// @ts-ignore
import request from '@/utils/request'
// 查询微生物数据列表
export function wswsampleList(query?: Object) {
return request({
url: '/lisworkgerm/wswsamplelist',
method: 'get',
params: query
})
}
// 查询微生物数据列表 分页
export function wswsamplelistpage(query?: Object) {
return request({
url: '/lisworkgerm/wswsamplelistpage',
method: 'get',
params: query
})
}
// 获取微生物样本信息
export function wswsampleinfo(query?: Object) {
return request({
url: '/lisworkgerm/germsampleinfo',
method: 'get',
params: query
})
}
// 获取微生物结果信息
export function sampleMedInfo(query?: Object) {
return request({
url: '/lisworkgerm/getresultlist',
method: 'get',
params: query
})
}
// 获取药敏数据
export function getresultmedList(query?: Object) {
return request({
url: '/lisworkgerm/getresultmedlist',
method: 'get',
params: query
})
}
// 获取专家评语字典
export function getgermtextdict(query?: Object) {
return request({
url: '/lisworkgerm/getgermtextdict',
method: 'get',
params: query
})
}
// 写入专家评语
export function savetestResult(query?: Object) {
return request({
url: '/lisworkgerm/savetestresult',
method: 'post',
data: query
})
}
// 获取操作记录
export function getComLog(query?: Object) {
return request({
url: '/lisworkgerm/getcomlog',
method: 'get',
params: query
})
}
//新增结果明细
export function newresult(data?: Object) {
return request({
url: '/lisworkgerm/newresult',
method: 'post',
data
})
}
//修改结果明细
export function changeresult(data?: Object) {
return request({
url: '/lisworkgerm/changeresult',
method: 'post',
data
})
}
//修改结果明细
export function saveresult(data?: Object) {
return request({
url: '/lisworkgerm/saveresult',
method: 'post',
data
})
}
//获取药敏字典
export function getmeddictList(data?: Object) {
return request({
url: '/lisworkgerm/getmeddict',
method: 'get',
params: data
})
}
//获取药敏模板
export function getmedgroupList(data?: Object) {
return request({
url: '/lisworkgerm/getmedgrouplist',
method: 'get',
params: data
})
}
//获取药敏模板明细
export function getmedgroupMbList(data?: Object) {
return request({
url: '/lisworkgerm/getmedgroup',
method: 'get',
params: data
})
}
//保存药敏结果
export function savemedresult(data?: Object) {
return request({
url: '/lisworkgerm/savemedresult',
method: 'post',
data
})
}
//保存所有药敏结果
export function savemedresultall(data?: Object) {
return request({
url: '/lisworkgerm/savemedresultall',
method: 'post',
data
})
}
//删除药敏结果
export function deletemedresult(data?: Object) {
return request({
url: '/lisworkgerm/deletemedresult',
method: 'post',
data
})
}
//获取培养基记录
export function getmediumList(data?: Object) {
return request({
url: '/lisworkgerm/getmedium',
method: 'get',
params: data
})
}
//获取培养基字典
export function getbactmediumList(data?: Object) {
return request({
url: '/lisworkgerm/getbactmedium',
method: 'get',
params: data
})
}
//新增培养基
export function insertmedium(data?: Object) {
return request({
url: '/lisworkgerm/insertmedium',
method: 'post',
data
})
}
//修改培养基
export function updatemedium(data?: Object) {
return request({
url: '/lisworkgerm/updatemedium',
method: 'post',
data
})
}
//删除培养基
export function deletemedium(data?: Object) {
return request({
url: '/lisworkgerm/deletemedium',
method: 'delete',
params: data
})
}
//删除培养基 Array
export function deletemediums(data?: Object) {
return request({
url: '/lisworkgerm/deletemedium',
method: 'delete',
data
})
}
//微生物报告审核
export function cbebCheck(data?: Object) {
return request({
url: '/lisworkgerm/check',
method: 'get',
params: data
})
}
//修改样本检验日期
export function changepatjyrq(data?: Object) {
return request({
url: '/lisworkgerm/changepatjyrq',
method: 'get',
params: data
})
}

View File

@ -1,70 +0,0 @@
// @ts-ignore
import request from '@/utils/request'
//获取项目信息
export function queryXmInfoList(query: any) {
return request({
url: '/reqInput/queryXmInfoList',
method: 'get',
params: query
})
}
//保存病人信息
export function saveReqInfo(query: any) {
return request({
url: '/reqInput/saveReqInfo',
method: 'post',
data: query
})
}
//保存病人信息
export function queryPatList(query: any) {
return request({
url: '/reqInput/queryPatList',
method: 'post',
data: query
})
}
//查询病人信息
export function queryPatInfo(query: any) {
return request({
url: '/reqInput/queryPatInfo',
method: 'get',
params: query
})
}
//不可开单项目列表
export function queryCreateReqList(query: any) {
return request({
url: '/reqInput/queryCreateReqList',
method: 'get',
params: query
})
}
//可开单项目列表
export function queryCanCreateReqList(query: any) {
return request({
url: '/reqInput/queryCanCreateReqList',
method: 'get',
params: query
})
}
//保存可开单项目
export function saveCanCreaeReqList(query: any) {
return request({
url: '/reqInput/saveCanCreaeReqList',
method: 'post',
data: query
})
}
//获取病人明细信息
export function queryPatDetail(query: any) {
return request({
url: '/reqInput/queryPatDetail',
method: 'get',
params: query
})
}

View File

@ -1,245 +0,0 @@
// @ts-ignore
import request from '@/utils/request'
// 保存数据
export function saveOpt(data?: Object) {
return request({
url: '/qc/saveOpt',
method: 'post',
data
})
}
// 查询项目的质控规则
export function queryRules(data?: Object) {
return request({
url: '/qc/queryRules',
method: 'get',
params: data
})
}
// 保存质控项目数据
export function saveQCItem(data?: Object) {
return request({
url: '/qc/saveQCItem',
method: 'post',
data
})
}
// 普通标本和质控标本对应关系
export function queryDataFromSample(data?: Object) {
return request({
url: '/qc/queryDataFromSample',
method: 'get',
params: data
})
}
// 查询质控品数据
export function queryQcSample(data?: Object) {
return request({
url: '/qc/queryQcSample',
method: 'get',
params: data
})
}
// 查询质控品数据
export function saveQcSample(data?: Object) {
return request({
url: '/qc/saveQcFromSample',
method: 'post',
data
})
}
// -删除普通标本和质控标本对应关系
export function deleteQcSample(data?: Object) {
return request({
url: '/qc/deleteQcFromSample',
method: 'get',
params: data
})
}
// 查询质控批号
export function queryBatch(data?: Object) {
return request({
url: '/qc/query',
method: 'get',
params: data
})
}
// 新增质控批号
export function addBatchno(data?: Object) {
return request({
url: '/qc/addBatchno',
method: 'post',
data
})
}
// 修改质控批号
export function updateBatchno(data?: Object) {
return request({
url: '/qc/updateBatchno',
method: 'post',
data
})
}
// 删除质控批号
export function delBatchno(data?: Object) {
return request({
url: '/qc/delBatchno',
method: 'delete',
params: data
})
}
// 查询质控批号 获取项目列表
export function queryBatchXm(data?: Object) {
return request({
url: '/qc/queryVal',
method: 'get',
params: data
})
}
// 获取项目列表
export function queryValXmdh(data?: Object) {
return request({
url: '/qc/queryValXmdh',
method: 'get',
params: data
})
}
// 保存质控品参数
export function saveBtQcSample(data?: Object) {
return request({
url: '/qc/saveQcSample',
method: 'post',
data
})
}
// 查询质控品录入
export function getqcval(data?: Object) {
return request({
url: '/qc/getqcvalitem',
method: 'get',
params: data
})
}
// 保存质控数据(当天所有项目)
export function saveqcvalItem(data?: Object) {
return request({
url: '/qc/saveqcvalitem',
method: 'post',
data
})
}
// 获取质控项目
export function queryXmItem(data?: Object) {
return request({
url: '/qc/queryitem',
method: 'get',
params: data
})
}
// 查询质控数据(单项目一个月)
export function getqcvalmonth(data?: Object) {
return request({
url: '/qc/getqcvalmonth',
method: 'get',
params: data
})
}
//保存质控数据(单项目一个月)
export function saveQcvalMonth(data?: Object) {
return request({
url: '/qc/saveqcvalmonth',
method: 'post',
data
})
}
//复制靶值
export function copyQcSample(data?: Object) {
return request({
url: '/qc/copyQcSample',
method: 'get',
params: data
})
}
//删除靶值
export function delQcSample(data?: Object) {
return request({
url: '/qc/delQcSample',
method: 'delete',
params: data
})
}
//查询质控室内数据(月统计)
export function getmonthst(data?: Object) {
return request({
url: '/qcstat/getmonthst',
method: 'get',
params: data
})
}
//查询月室内质控小结
export function getmonthpj(data?: Object) {
return request({
url: '/qcstat/getmonthpj',
method: 'get',
params: data
})
}
//保存月室内质控小结
export function savemonthpj(data?: Object) {
return request({
url: '/qcstat/savemonthpj',
method: 'post',
data
})
}
//查询月失控情况总结
export function getmonthskzj(data?: Object) {
return request({
url: '/qcstat/getmonthskzj',
method: 'get',
params: data
})
}
//保存月失控情况总结
export function savemonthskzj(data?: Object) {
return request({
url: '/qcstat/savemonthskzj',
method: 'post',
data
})
}
//查询质控数据
export function qcgraphList(data?: Object) {
return request({
url: '/qcgraph/query',
method: 'get',
params: data
})
}
//查询质控参数列表
export function querySample(data?: Object) {
return request({
url: '/qcgraph/querysample',
method: 'get',
params: data
})
}
//计算靶值
export function qcCalc(data?: Object) {
return request({
url: '/qcgraph/calc',
method: 'get',
params: data
})
}
//质量汇总查询
export function ncisQuery(data?: Object) {
return request({
url: '/ncis/query',
method: 'get',
params: data
})
}

View File

@ -0,0 +1,59 @@
import request from '@/utils/request'
// 查询数据列表
export function queryLabPatList(query) {
return request({
url: '/liswork/samplelist',
method: 'get',
params: query
})
}
// 查询数据
export function queryLabPat(query) {
return request({
url: '/liswork/sampleinfo',
method: 'get',
params: query
})
}
// 查询服务器时间
export function getDate() {
return request({
url: '/liswork/getdate',
method: 'get',
})
}
//增加字典数据
export function addLabPatService(data) {
return request({
url: '/liswork/add',
method: 'post',
data
})
}
//修改字典数据
export function updateLabPatService(data) {
return request({
url: '/liswork/update',
method: 'post',
data
})
}
// 查询数据列表
export function queryLabResults(query) {
return request({
url: '/liswork/resultinfo',
method: 'get',
params: query
})
}
// 批量删除结果
export function deleteresult(data) {
return request({
url: '/liswork/deleteresult',
method: 'post',
data: data
})
}

View File

@ -1,491 +0,0 @@
// @ts-ignore
import request from '@/utils/request'
import { ElLoading, ElMessage } from 'element-plus'
// 查询数据列表
export function queryLabPatList(query?: Object) {
return request({
url: '/liswork/samplelist',
method: 'get',
params: query
})
}
// 查询数据
export function queryLabPat(query?: Object) {
return request({
url: '/liswork/sampleinfo',
method: 'get',
params: query
})
}
// 查询服务器时间
export function getDate() {
return request({
url: '/liswork/getdate',
method: 'get',
})
}
//新增结果明细
export function newresult(data?: Object) {
return request({
url: '/lisworkoper/newresult',
method: 'post',
data
})
}
//修改结果明细
export function changeresult(data?: Object) {
return request({
url: '/lisworkoper/changeresult',
method: 'post',
data
})
}
//新建样本病人信息
export function updateLabPat(data?: Object) {
return request({
url: '/lisworkoper/changepat',
method: 'post',
data
})
}
// 修改病人信息
export function changepatcolumn(query?: Object) {
return request({
url: '/lisworkoper/changepatcolumn',
method: 'get',
params: query
})
}
// 查询结果数据列表
export function queryLabResults(query?: Object) {
return request({
url: '/liswork/resultinfo',
method: 'get',
params: query
})
}
// 查询检验项目
export function queryXmInfo(query?: Object) {
return request({
url: '/liswork/queryxminfo',
method: 'get',
params: query
})
}
// 查询常用取值
export function queryXmVal(query?: Object) {
return request({
url: '/liswork/queryxmval',
method: 'get',
params: query
})
}
// 批量删除结果
export function deleteresult(data?: Object) {
return request({
url: '/liswork/deleteresult',
method: 'post',
data: data
})
}
//审核
export function check2(query?: Object) {
return request({
url: '/lisworkoper/check2',
method: 'get',
params: query
})
}
//取消审核
export function uncheck2(query?: Object) {
return request({
url: '/lisworkoper/uncheck2',
method: 'get',
params: query
})
}
//取消审核原因
export function unconfirmlog(query?: Object) {
return request({
url: '/lisworkoper/unconfirmlog',
method: 'get',
params: query
})
}
//取消审核原因
export function check1(query?: Object) {
return request({
url: 'lisworkoper/check1',
method: 'get',
params: query
})
}
// 获取操作userId
export function checkuser(query?: Object) {
return request({
url: '/liswork/checkuserid',
method: 'get',
params: query
})
}
export function reglimit(query?: Object) {
return request({
url: '/lisworkoper/reglimit',
method: 'get',
params: query
})
}
export function printsample(query?: Object) {
return request({
url: '/lisworkoper/printsample',
method: 'get',
params: query
})
}
// 打印报告单
export function printListwork(query?: Object) {
// 1. 开启 loading 效果
const loadingInstance = ElLoading.service({
lock: true,
text: '数据加载中...',
background: 'rgba(0, 0, 0, 0.7)'
})
// 2. 发起请求并处理 loading 关闭
return request({
url: '/liswork/print',
method: 'get',
params: query
}).finally(() => {
loadingInstance.close()
}).catch((error: any) => {
ElMessage.error('数据加载失败,请稍后重试')
throw error
})
}
// 查询默认值
export function loadDefault(query?: Object) {
return request({
url: '/lisworkoper/loaddefault',
method: 'get',
params: query
})
}
// 历史对照
export function historyInfo(query?: Object) {
return request({
url: '/lisworkpageinfo/history',
method: 'get',
params: query
})
}
// 其他结果
export function otherinstr(query?: Object) {
return request({
url: '/lisworkpageinfo/otherinstr',
method: 'get',
params: query
})
}
// 其他结果明细
export function otherInstrDetail(query?: Object) {
return request({
url: '/lisworkpageinfo/otherinstrdetail',
method: 'get',
params: query
})
}
// 收费信息
export function reqdetail(query?: Object) {
return request({
url: '/lisworkpageinfo/reqdetail',
method: 'get',
params: query
})
}
// 获取收费信息报告明细
export function reqdetailmx(query?: Object) {
return request({
url: '/lisworkpageinfo/reqdetailmx',
method: 'get',
params: query
})
}
// 左边form参数
export function instrdconfig(query?: Object) {
return request({
url: '/liswork/instrdconfig',
method: 'get',
params: query
})
}
// 医生校验
export function checkYsUser(query?: Object) {
return request({
url: '/liswork/checkuserid',
method: 'get',
params: query
})
}
// 删除样本
export function delSample(query?: Object) {
return request({
url: '/lisworkoper/delsample',
method: 'get',
params: query
})
}
// 获取标本流转信息
export function reqinfo(query?: Object) {
return request({
url: '/lisworkpageinfo/reqinfo',
method: 'get',
params: query
})
}
// 获取检验结果图片信息
export function resultgraph(query?: Object) {
return request({
url: '/lisworkpageinfo/resultgraph',
method: 'get',
params: query
})
}
// 获取组合项目模板
export function inputmdl(query?: Object) {
return request({
url: '/lisworkpageinfo/inputmdl',
method: 'get',
params: query
})
}
// 项目模板输入结果
export function setinputmdl(query?: Object) {
return request({
url: '/liswork/setinputmdl',
method: 'get',
params: query
})
}
// 保存病人结果
export function saveResult(query?: Object) {
return request({
url: '/lisworkoper/saveresult',
method: 'post',
data: query
})
}
// 获取复查结果
export function redoResult(query?: Object) {
return request({
url: '/lisworkpageinfo/redo',
method: 'get',
params: query
})
}
// 保存复查结果
export function saveRedo(query?: Object) {
return request({
url: '/lisworkpageinfo/saveredo',
method: 'post',
data: query
})
}
// 交换复查结果
export function swapRedo(query?: Object) {
return request({
url: '/lisworkpageinfo/swapredo',
method: 'post',
data: query
})
}
// 删除复查结果
export function delRedo(query?: Object) {
return request({
url: '/lisworkpageinfo/deleteRedo',
method: 'post',
data: query
})
}
// 仪器组可操作仪器查询
export function getGroupInstrdList(query?: Object) {
return request({
url: '/liswork/getGroupInstrdList',
method: 'get',
params: query
})
}
// 结果修改记录
export function getresultchangelog(query?: Object) {
return request({
url: '/liswork/getresultchangelog',
method: 'get',
params: query
})
}
// 获取临床意义
export function clinicInfo(query?: Object) {
return request({
url: '/lisworkpageinfo/clinicref',
method: 'get',
params: query
})
}
// 指定为某样本复查结果
export function setredoSample(query?: Object) {
return request({
url: '/lisworkoper/setredosample',
method: 'get',
params: query
})
}
// 锁定报告
export function lockSample(query?: Object) {
return request({
url: '/lisworkoper/locksample',
method: 'get',
params: query
})
}
// 解锁报告
export function unLockSample(query?: Object) {
return request({
url: '/lisworkoper/unlocksample',
method: 'get',
params: query
})
}
// 查看电子病历
export function emrquery(query?: Object) {
return request({
url: '/liswork/emrquery',
method: 'get',
params: query
})
}
// 查看患者360视图
export function allPatquery(query?: Object) {
return request({
url: '/liswork/allpatquery',
method: 'get',
params: query
})
}
// 双工流水线
export function datalink(query?: Object) {
return request({
url: '/liswork/datalink',
method: 'get',
params: query
})
}
// 批量更改流水线状态
export function changeLinkList(query?: Object) {
return request({
url: '/liswork/changelinklist',
method: 'post',
data: query
})
}
// 清除所有未做项目
export function clearnotComplete(query?: Object) {
return request({
url: '/liswork/clearnotcomplete',
method: 'get',
params: query
})
}
// 查看备份数据
export function viewBackup(query?: Object) {
return request({
url: '/liswork/viewbackup',
method: 'get',
params: query
})
}
// 未建项目结果
export function noiteMresult(query?: Object) {
return request({
url: '/liswork/noitemresult',
method: 'get',
params: query
})
}
// 项目与通道号对照
export function setitemInter(query?: Object) {
return request({
url: '/liswork/setiteminter',
method: 'get',
params: query
})
}
// 清除打印标志
export function clearPrintflag(query?: Object) {
return request({
url: '/liswork/clearprintflag',
method: 'get',
params: query
})
}
// 自动签收条码
export function autosavebarcode(query?: Object) {
return request({
url: '/liswork/autosavebarcode',
method: 'get',
params: query
})
}
// 条码签收
export function querybarcode(query?: Object) {
return request({
url: '/liswork/querybarcode',
method: 'get',
params: query
})
}
// 手动保存签收条码
export function saveBarcode(query?: Object) {
return request({
url: '/liswork/savebarcode',
method: 'post',
data: query
})
}
// 拒收条码
export function refusedbarcode(query?: Object) {
return request({
url: '/refused/getData',
method: 'get',
params: query
})
}
// 保存拒收条码
export function refusedSaveData(query?: Object) {
return request({
url: '/refused/saveData',
method: 'post',
data: query
})
}
// 查询申请单全程信息
export function wholeprocessList(query?: Object) {
return request({
url: '/wholeprocess/list',
method: 'get',
params: query
})
}

View File

@ -1,41 +0,0 @@
//知识库
// @ts-ignore
import request from '@/utils/request'
//查询数据
export function queryService(params:any){
return request({
url: '/xmKnowledge/query',
method: 'get',
params
})
}
//查询明细数据
export function queryDetailService(xmdh: string){
return request({
url: '/xmKnowledge/detail',
method: 'get',
params: {
xmdh
}
})
}
//新增数据
export function addService(params:any){
return request({
url: '/xmKnowledge/add',
method: 'post',
params
})
}
//删除数据
export function delService(params:any){
return request({
url: '/xmKnowledge/del',
method: 'get',
params
})
}

View File

@ -1,150 +0,0 @@
//@ts-ignore js语法检查忽略
import request from '@/utils/request';
// 查询模板列表
export function queryMbList(query?: Object) {
return request({
url: '/labInputmdl/query',
method: 'get',
params: query,
});
}
// 查询明细数据
export function queryDetail(query?: Object) {
return request({
url: '/labInputmdl/queryDetail',
method: 'get',
params: query,
});
}
// 新增模板
export function addXmInfo(query?: Object) {
return request({
url: '/labInputmdl/add',
method: 'post',
data: query,
});
}
// 修改模板
export function updateXmInfo(query?: Object) {
return request({
url: '/labInputmdl/update',
method: 'post',
data: query,
});
}
// 删除模板
export function deleteXmInfo(query?: Object) {
return request({
url: '/labInputmdl/delete',
method: 'get',
params: query,
});
}
// 删除明细数据
export function deleteDetail(query?: Object) {
return request({
url: '/labInputmdl/deleteDetail',
method: 'post',
data: query,
});
}
// 查询项目组合
export function xmCompQuery(query?: Object) {
return request({
url: '/xmComp/query',
method: 'get',
params: query,
});
}
// 增加项目组合
export function addxmComp(query?: Object) {
return request({
url: '/xmComp/add',
method: 'post',
data: query,
});
}
// 修改项目组合
export function editxmComp(query?: Object) {
return request({
url: '/xmComp/update',
method: 'post',
data: query,
});
}
// 删除项目组合
export function delxmComp(query?: Object) {
return request({
url: '/xmComp/del',
method: 'get',
params: query,
});
}
// 查询明细数据
export function queryDetailComp(query?: Object) {
return request({
url: '/xmComp/queryDetail',
method: 'get',
params: query,
});
}
// 删除明细数据
export function deleteDetailComp(query?: Object) {
return request({
url: '/xmComp/deleteDetail',
method: 'post',
data: query,
});
}
// 查询仪器组合列表
export function queryInstrgroupList(query?: Object) {
return request({
url: '/instrgroup/query',
method: 'get',
params: query,
});
}
// 新增仪器组合
export function addInstrgroup(query?: Object) {
return request({
url: '/instrgroup/add',
method: 'post',
data: query,
});
}
// 修改仪器组合
export function updateInstrgroup(query?: Object) {
return request({
url: '/instrgroup/update',
method: 'post',
data: query,
});
}
// 删除仪器组合
export function deleteInstrgroup(query?: Object) {
return request({
url: '/instrgroup/delete',
method: 'get',
params: query,
});
}
// 查询明细数据
export function queryDetailInstrgroup(query?: Object) {
return request({
url: '/instrgroup/queryDetail',
method: 'get',
params: query,
});
}
// 删除明细数据
export function delDetailInstrgroup(query?: Object) {
return request({
url: '/instrgroup/deleteDetail',
method: 'post',
data: query,
});
}

View File

@ -1,55 +0,0 @@
// 抗生素折点范围字典 API
import request from '@/utils/request'
// 获取菌属列表(从 xm_meddetail 表)
export function getGermClassList(params?: any) {
return request({
url: '/xm/meddetail/germclass/list',
method: 'get',
params
})
}
// 获取抗生素字典列表(从 xm_med 表)
export function getAntibioticList(params?: any) {
return request({
url: '/xm/med/list',
method: 'get',
params
})
}
// 获取折点范围数据
export function getBreakpointList(params: any) {
return request({
url: '/xm/breakpoint/list',
method: 'get',
params
})
}
// 保存折点范围
export function saveBreakpoint(data: any) {
return request({
url: '/xm/breakpoint',
method: 'post',
data
})
}
// 批量保存折点范围
export function batchSaveBreakpoint(data: any[]) {
return request({
url: '/xm/breakpoint/batch',
method: 'post',
data
})
}
// 删除折点范围
export function deleteBreakpoint(id: number) {
return request({
url: `/xm/breakpoint/${id}`,
method: 'delete'
})
}

View File

@ -1,197 +0,0 @@
//@ts-ignore js语法检查忽略
import request from '@/utils/request';
// 查询参数列表
export function paramList(query?: Object) {
return request({
url: '/param/query',
method: 'get',
params: query,
});
}
// 获取默认值
export function getdef(query?: Object) {
return request({
url: '/param/getdef',
method: 'get',
params: query,
});
}
// 查询某个参数的值
export function queryValue(query?: Object) {
return request({
url: '/param/queryValue',
method: 'get',
params: query,
});
}
// 修改参数列表
export function updateParam(query?: Object) {
return request({
url: '/param/update',
method: 'put',
data: query,
});
}
// 删除参数列表
export function delParam(query?: Object) {
return request({
url: '/param/del',
method: 'delete',
params: query,
});
}
// 查询参数列表树状结构
export function paramTree(query?: Object) {
return request({
url: '/param/queryTree',
method: 'get',
params: query,
});
}
// 查询某个参数
export function queryOneService(yq: string, xxdh: string) {
return request({
url: '/param/queryOne',
method: 'get',
params: { yq, xxdh }
});
}
// 查询滤光片波长对照表
export function queryFilter(data?: Object) {
return request({
url: '/xminfo/queryFilter',
method: 'get',
params: data
});
}
// 新增滤光片波长对照表
export function addXmFilter(data?: Object) {
return request({
url: '/xminfo/addXmFilter',
method: 'post',
data
});
}
// 删除滤光片波长对照表
export function delXmFilter(data?: Object) {
return request({
url: '/xminfo/delXmFilter',
method: 'get',
params: data
});
}
// 获取试剂维护数据
export function queryXmLot(data?: Object) {
return request({
url: '/xminfo/queryXmLot',
method: 'get',
params: data
});
}
// 新增试剂维护数据
export function addXmLot(data?: Object) {
return request({
url: '/xminfo/addXmLot',
method: 'post',
data
});
}
// 删除试剂维护数据
export function delXmLot(data?: Object) {
return request({
url: '/xminfo/delXmLot',
method: 'get',
params: data
});
}
// 获取试剂厂家
export function querySjcs(data?: Object) {
return request({
url: '/xminfo/querySjcs',
method: 'get',
params: data
});
}
// 查询检验项目排序
export function getitemsort(data?: Object) {
return request({
url: '/xminfo/getitemsort',
method: 'get',
params: data
});
}
// 保存检验项目排序
export function saveitemsort(data?: Object) {
return request({
url: '/xminfo/saveitemsort',
method: 'post',
data
});
}
// 查询检验项目通道号
export function getitemtdh(data?: Object) {
return request({
url: '/xminfo/getitemtdh',
method: 'get',
params: data
});
}
// 保存检验项目通道号
export function saveitemtdh(data?: Object) {
return request({
url: '/xminfo/saveitemtdh',
method: 'post',
data
});
}
// 删除检验项目通道号
export function delitemtdh(data?: Object) {
return request({
url: '/xminfo/delitemtdh',
method: 'get',
params: data
});
}
// 获取仪器参数
export function querydouble(data?: Object) {
return request({
url: '/xminfo/querydouble',
method: 'get',
params: data
});
}
// 保存仪器参数
export function savedouble(data?: Object) {
return request({
url: '/xminfo/savedouble',
method: 'post',
data
});
}
// 获取参数默认模板
export function querymbmc(data?: Object) {
return request({
url: '/xminfo/querymbmc',
method: 'get',
params: data
});
}
// 查询调整列表
export function queryInputcol(data?: Object) {
return request({
url: '/inputcol/query',
method: 'get',
params: data
});
}
// 整体调整列表
export function saveInputcol(data?: Object) {
return request({
url: '/inputcol/save',
method: 'put',
data: data
});
}

View File

@ -1,39 +0,0 @@
//报告单设置界面
// @ts-ignore
import request from '@/utils/request'
//新增数据
export function addReportService(LabReportInstr: Object){
return request({
url: '/labReportinstr/add',
method: 'post',
data: LabReportInstr
})
}
//查询数据
export function queryReportService(LabReportInstr: Object){
return request({
url: '/labReportinstr/query',
method: 'get',
params: LabReportInstr
})
}
//修改数据
export function updateReportService(LabReportInstr: Object){
return request({
url: '/labReportinstr/update',
method: 'post',
data: LabReportInstr
})
}
//删除数据
export function deleteReportService(LabReportInstr: Object){
return request({
url: '/labReportinstr/del',
method: 'get',
params: LabReportInstr
})
}

View File

@ -1,83 +0,0 @@
//参数设置字典
// @ts-ignore
import request from '@/utils/request'
// 查询字典列表
export function liststatsparam(params: any) {
return request({
url: '/statstemp/param/list',
method: 'get',
params
})
}
// 查询字典详细
export function getstatsparam(id: number) {
return request({
url: `/statstemp/param/${id}`,
method: 'get'
})
}
// 新增字典
export function addstatsparam(params: any) {
return request({
url: '/statstemp/param',
method: 'post',
data: params
})
}
// 修改字典
export function updatestatsparam(params: any) {
return request({
url: '/statstemp/param',
method: 'put',
data: params
})
}
// 删除字典
export function delstatsparam(id: any) {
return request({
url: `/statstemp/param/${id}`,
method: 'delete'
})
}
// 获取模板详细信息
export function getstatsTemplate(params: any) {
return request({
url: '/stats/gettemplate',
method: 'get',
params
})
}
// 模板参数查询详细信息
export function getstatsQuery(params: any) {
return request({
url: '/stats/query',
method: 'post',
data: params
})
}
// 打印模板数据
export function printTemplate(params: any) {
return request({
url: '/stats/print',
method: 'post',
data: params,
responseType: 'blob'
})
}
// 导出excel
export function exportTemplate(params: any) {
return request({
url: '/stats/exportExcel',
method: 'post',
data: params,
responseType: 'blob'
})
}

View File

@ -1,55 +0,0 @@
//参数设置字典
// @ts-ignore
import request from '@/utils/request'
// 查询字典列表
export function liststatstemp(params: any) {
return request({
url: '/statstemp/list',
method: 'get',
params
})
}
// 复制模板
export function copyParameter(params: any) {
return request({
url: '/statstemp/param/copyparameter',
method: 'get',
params
})
}
// 查询字典详细
export function getstatstemp(id: number) {
return request({
url: `/statstemp/${id}`,
method: 'get'
})
}
// 新增字典
export function addstatstemp(params: any) {
return request({
url: '/statstemp',
method: 'post',
data: params
})
}
// 修改字典
export function updatestatstemp(params: any) {
return request({
url: '/statstemp',
method: 'put',
data: params
})
}
// 删除字典
export function delstatstemp(id: any) {
return request({
url: `/statstemp/${id}`,
method: 'delete'
})
}

View File

@ -1,46 +0,0 @@
//参数设置字典
// @ts-ignore
import request from '@/utils/request'
// 查询字典列表
export function listconfigdict(params:any) {
return request({
url: '/configdict/list',
method: 'get',
params
})
}
// 查询字典详细
export function getconfigdict(id:number) {
return request({
url: `/configdict/${id}`,
method: 'get'
})
}
// 新增字典
export function addconfigdict(params:any) {
return request({
url: '/configdict',
method: 'post',
data:params
})
}
// 修改字典
export function updateconfigdict(params:any) {
return request({
url: '/configdict',
method: 'put',
data:params
})
}
// 删除字典
export function delconfigdict(id:any) {
return request({
url: `/configdict/${id}`,
method: 'delete'
})
}

View File

@ -1,45 +0,0 @@
//本地参数设置
// @ts-ignore
import request from '@/utils/request'
// 查询字典列表
export function listlocalconfig(params:any) {
return request({
url: '/localconfig/list',
method: 'get',
params
})
}
export function getLocalConfigDefault(params:any) {
return request({
url: '/localconfig/resetdef',
method: 'get',
params
})
}
// 查询字典下拉框内容
export function getoptionselect(params:any) {
return request({
url: '/localconfig/optionselect',
method: 'get',
params
})
}
// 修改字典
export function updatelocalconfig(params:any) {
return request({
url: '/localconfig',
method: 'put',
data:params
})
}
// 批量保存所有结果
export function savelocalconfig(params:any) {
return request({
url: '/localconfig/editall',
method: 'put',
data:params
})
}

View File

@ -1,63 +0,0 @@
//参数设置字典
// @ts-ignore
import request from '@/utils/request'
// 查询字典列表
export function listreportdict(params:any) {
return request({
url: '/reportdict/list',
method: 'get',
params
})
}
// 查询字典详细
export function getreportdict(id:number) {
return request({
url: `/reportdict/${id}`,
method: 'get'
})
}
// 新增字典
export function addreportdict(params:any) {
return request({
url: '/reportdict',
method: 'post',
data:params
})
}
// 修改字典
export function updatereportdict(params:any) {
return request({
url: '/reportdict',
method: 'put',
data:params
})
}
// 删除字典
export function delreportdict(id:any) {
return request({
url: `/reportdict/${id}`,
method: 'delete'
})
}
// 自动加载
export function autoload() {
return request({
url: '/reportdict/autoload',
method: 'get'
})
}
// 查询字典列表
export function optionselect(params:any) {
return request({
url: '/reportdict/optionselect',
method: 'get',
params
})
}

View File

@ -1,47 +0,0 @@
//参数设置字典
// @ts-ignore
import request from '@/utils/request'
// 查询字典列表
export function listreportinstr(params:any) {
return request({
url: '/reportinstr/list',
method: 'get',
params
})
}
// 查询字典详细
export function getreportinstr(id:number) {
return request({
url: `/reportinstr/${id}`,
method: 'get'
})
}
// 新增字典
export function addreportinstr(params:any) {
return request({
url: '/reportinstr',
method: 'post',
data:params
})
}
// 修改字典
export function updatereportinstr(params:any) {
return request({
url: '/reportinstr',
method: 'put',
data:params
})
}
// 删除字典
export function delreportinstr(id:any) {
return request({
url: `/reportinstr/${id}`,
method: 'delete'
})
}

View File

@ -1,39 +0,0 @@
//仪器收费项目对照
// @ts-ignore
import request from '@/utils/request'
//查询已分配数据
export function queryService(params:any){
return request({
url: '/feeinstr/query',
method: 'get',
params
})
}
//查询未分配的项目数据
export function queryXmService(params:any){
return request({
url: '/feeinstr/queryXm',
method: 'get',
params
})
}
//新增数据
export function addService(params:any){
return request({
url: '/feeinstr/add',
method: 'post',
params
})
}
//删除数据
export function delService(params:any){
return request({
url: '/feeinstr/del',
method: 'delete',
params
})
}

View File

@ -1,54 +0,0 @@
//收费项目报告项目对照
// @ts-ignore
import request from "@/utils/request";
//查询申请信息列表
export function queryListService(params: any) {
return request({
url: "/vsReportitem/queryList",
method: "get",
params,
});
}
//查询项目明细
export function queryDetailService(params: any) {
return request({
url: "/vsReportitem/query",
method: "get",
params,
});
}
//查询项目明细 old
export function queryOldxminfo(params: any) {
return request({
url: "/oldxminfo/query",
method: "get",
params,
});
}
//搜索项目
export function searchxminfo(params: any) {
return request({
url: "/vsReportitem/search",
method: "get",
params,
});
}
//添加收费项目报告项目对照
export function addXminfo(data: Object) {
return request({
url: "/vsReportitem/add",
method: "post",
data,
});
}
//删除收费项目报告项目对照
export function delXminfo(data: Object) {
return request({
url: "/vsReportitem/del",
method: "delete",
params: data,
});
}

View File

@ -1,13 +1,12 @@
import request from '@/utils/request'
// 登录方法
export function login(username, password, code, uuid,loginParam) {
export function login(username, password, code, uuid) {
const data = {
username,
password,
code,
uuid,
loginParam
uuid
}
return request({
url: '/login',
@ -20,26 +19,6 @@ export function login(username, password, code, uuid,loginParam) {
})
}
// 单点登录
export function loginAnonymous(data) {
return request({
url: '/loginAnonymous',
method: 'post',
data: data
})
}
export function getLocalconfig(data) {
return request({
url: '/getLocalconfig',
headers: {
isToken: false
},
method: 'get',
params: data,
timeout: 20000
})
}
// 注册方法
export function register(data) {
return request({

View File

@ -23,18 +23,18 @@ export function printBarcodeall(data) {
data: data
})
}
// 查询病人列表
export function queryPatList() {
// 查询采样登记详细
export function getReqmain(sqh) {
return request({
url: '/mzcx/queryPatList',
url: '/system/reqmain/' + sqh,
method: 'get'
})
}
// 打印回单
export function printBackpaper(data) {
// 新增采样登记
export function addReqmain(data) {
return request({
url: '/mzcx/printBackpaper',
url: '/system/reqmain',
method: 'post',
data: data
})

View File

@ -1,155 +0,0 @@
//@ts-ignore js语法检查忽略
import request from '@/utils/request';
// 查询条码类别
export function feeitemclassList(query?: Object) {
return request({
url: 'feeitemclass/query',
method: 'get',
params: query,
});
}
// 报告领取规则
export function rptgetrules(query?: Object) {
return request({
url: '/rptgetrule/query',
method: 'get',
params: query,
});
}
// 新增条码类别
export function feeitemclassAdd(query?: Object) {
return request({
url: '/feeitemclass/add',
method: 'post',
data: query,
});
}
// 修改条码类别
export function feeitemclassUpdate(query?: Object) {
return request({
url: '/feeitemclass/update',
method: 'post',
data: query,
});
}
// 删除条码类别
export function feeitemclassDel(query?: Object) {
return request({
url: '/feeitemclass/del',
method: 'delete',
params: query,
});
}
// 收费项目维护列表
export function xmfeeList(query?: Object) {
return request({
url: 'xmfee/query',
method: 'get',
params: query,
});
}
// 收费项目新增
export function xmfeeadd(query?: Object) {
return request({
url: 'xmfee/addXmfee',
method: 'post',
data: query,
});
}
// 收费项目修改
export function xmfeeUpdate(query?: Object) {
return request({
url: 'xmfee/update',
method: 'post',
data: query,
});
}
// 收费项目删除
export function xmfeeDel(query?: Object) {
return request({
url: 'xmfee/del',
method: 'delete',
params: query,
});
}
// 报告领取规则维护列表
export function rptgetruleList(query?: Object) {
return request({
url: 'rptgetrule/query',
method: 'get',
params: query,
});
}
// 报告领取规则明细列表
export function rptgetruleDetail(query?: Object) {
return request({
url: 'rptgetrule/queryDetail',
method: 'get',
params: query,
});
}
// 报告领取规则新增
export function rptgetruleadd(query?: Object) {
return request({
url: 'rptgetrule/add',
method: 'post',
data: query,
});
}
// 报告领取规则修改
export function rptgetruleUpdate(query?: Object) {
return request({
url: 'rptgetrule/update',
method: 'post',
data: query,
});
}
// 报告领取规则删除
export function rptgetruleDel(query?: Object) {
return request({
url: 'rptgetrule/del',
method: 'delete',
params: query,
});
}
// 报告领取规则明细删除
export function rptgetruleDelDetail(query?: Object) {
return request({
url: 'rptgetrule/delDetail',
method: 'delete',
params: query,
});
}
// 查询条码类别与项目对照
export function feeitemvsList(query?: Object) {
return request({
url: '/feeitemvsclass/query',
method: 'get',
params: query,
});
}
// 保存对照的项目
export function feeitemvAdd(query?: Object) {
return request({
url: '/feeitemvsclass/add',
method: 'post',
data: query,
});
}
// 保存对照的项目
export function feeitemvDel(query?: Object) {
return request({
url: '/feeitemvsclass/del',
method: 'delete',
params: query,
});
}

43
src/api/pinyin.js Normal file
View File

@ -0,0 +1,43 @@
// pinyin.js - 拼音转换工具
// src/utils/pinyin-utils.js
import { pinyin } from 'pinyin-pro';
/**
* 获取文本的拼音首字母
* @param {string} text - 要转换的文本
* @returns {string} - 拼音首字母字符串
*/
export function getFirstLetter(text) {
if (!text) return '';
// 获取拼音(不带音调,数组形式)
const pinyinArray = pinyin(text, { toneType: "none", type: "array" });
// 提取每个拼音的首字母并连接
return pinyinArray.map(word => word.charAt(0)).join('');
}
/**
* 获取文本的拼音全拼
* @param {string} text - 要转换的文本
* @returns {string} - 拼音全拼字符串
*/
export function getFullPinyin(text) {
if (!text) return '';
// 获取拼音(不带音调,字符串形式)
return pinyin(text, { toneType: "none" }).replace(/\s+/g, '');
}
/**
* 处理选项数据,添加拼音和首字母属性
* @param {Array} options - 原始选项数组,每个元素包含id和text属性
* @returns {Array} - 处理后的选项数组,每个元素包含pinyin和firstLetters属性
*/
export function processOptions(options) {
return options.map(option => ({
...option,
pinyin: getFullPinyin(option.text),
firstLetters: getFirstLetter(option.text)
}));
}

View File

@ -1,44 +0,0 @@
import request from '@/utils/request'
// 查询诊断列表
export function listdiagnosis(query) {
return request({
url: '/transitdict/diagnosis/list',
method: 'get',
params: query
})
}
// 查询诊断详细
export function getdiagnosis(diagnosisId) {
return request({
url: '/transitdict/diagnosis/' + diagnosisId,
method: 'get'
})
}
// 新增诊断
export function adddiagnosis(data) {
return request({
url: '/transitdict/diagnosis',
method: 'post',
data: data
})
}
// 修改诊断
export function updatediagnosis(data) {
return request({
url: '/transitdict/diagnosis',
method: 'put',
data: data
})
}
// 删除诊断
export function deldiagnosis(diagnosisId) {
return request({
url: '/transitdict/diagnosis/' + diagnosisId,
method: 'delete'
})
}

View File

@ -1,44 +0,0 @@
import request from '@/utils/request'
// 查询医疗机构列表
export function listhospital(query) {
return request({
url: '/transitdict/hospital/list',
method: 'get',
params: query
})
}
// 查询医疗机构详细
export function gethospital(hospitalId) {
return request({
url: '/transitdict/hospital/' + hospitalId,
method: 'get'
})
}
// 新增医疗机构
export function addhospital(data) {
return request({
url: '/transitdict/hospital',
method: 'post',
data: data
})
}
// 修改医疗机构
export function updatehospital(data) {
return request({
url: '/transitdict/hospital',
method: 'put',
data: data
})
}
// 删除医疗机构
export function delhospital(hospitalId) {
return request({
url: '/transitdict/hospital/' + hospitalId,
method: 'delete'
})
}

View File

@ -1,44 +0,0 @@
import request from '@/utils/request'
// 查询诊断列表
export function listitemclass(query) {
return request({
url: '/transitdict/itemclass/list',
method: 'get',
params: query
})
}
// 查询诊断详细
export function getitemclass(itemclassId) {
return request({
url: '/transitdict/itemclass/' + itemclassId,
method: 'get'
})
}
// 新增诊断
export function additemclass(data) {
return request({
url: '/transitdict/itemclass',
method: 'post',
data: data
})
}
// 修改诊断
export function updateitemclass(data) {
return request({
url: '/transitdict/itemclass',
method: 'put',
data: data
})
}
// 删除诊断
export function delitemclass(itemclassId) {
return request({
url: '/transitdict/itemclass/' + itemclassId,
method: 'delete'
})
}

View File

@ -1,44 +0,0 @@
import request from '@/utils/request'
// 查询字典列表
export function listregdict(query) {
return request({
url: '/transitdict/regdict/list',
method: 'get',
params: query
})
}
// 查询字典详细
export function getregdict(regdictId) {
return request({
url: '/transitdict/regdict/' + regdictId,
method: 'get'
})
}
// 新增字典
export function addregdict(data) {
return request({
url: '/transitdict/regdict',
method: 'post',
data: data
})
}
// 修改字典
export function updateregdict(data) {
return request({
url: '/transitdict/regdict',
method: 'put',
data: data
})
}
// 删除字典
export function delregdict(regdictId) {
return request({
url: '/transitdict/regdict/' + regdictId,
method: 'delete'
})
}

View File

@ -1,59 +0,0 @@
// @ts-ignore
import request from '@/utils/request'
// 获取流转数据
export function querySample(query?: Object) {
return request({
url: '/transit/querySample',
method: 'get',
params: query
})
}
// 获取未签收的标本
export function querySampleSign(query?: Object) {
return request({
url: '/transit/querySampleSign',
method: 'get',
params: query
})
}
// 签收标本
export function sampleSign(query?: Object) {
return request({
url: '/transit/sampleSign',
method: 'post',
data: query
})
}
// 退回的标本
export function querySampleSignBack(query?: Object) {
return request({
url: '/transit/querySampleSignBack',
method: 'get',
params: query
})
}
// 标本报表
export function sampleTypeCount(query?: Object) {
return request({
url: '/transitReport/sampleTypeCount',
method: 'get',
params: query
})
}
// 标本报表费用
export function sampleitemCount(query?: Object) {
return request({
url: '/transitReport/itemCount',
method: 'get',
params: query
})
}
// 标本报表费用
export function getallcount(query?: Object) {
return request({
url: '/transitstat/getallcount',
method: 'get',
params: query
})
}

View File

@ -1,53 +0,0 @@
//@ts-ignore js语法检查忽略
import request from '@/utils/request';
// 查询参数列表
export function paramList(query?: Object) {
return request({
url: '/param/query',
method: 'get',
params: query,
});
}
// 新增参数列表
export function addParam(query?: Object) {
return request({
url: '/param/add',
method: 'post',
data: query,
});
}
// 修改参数列表
export function updateParam(query?: Object) {
return request({
url: '/param/update',
method: 'post',
data: query,
});
}
// 删除参数列表
export function delParam(query?: Object) {
return request({
url: '/param/del',
method: 'delete',
params: query,
});
}
// 查询参数列表树状结构
export function paramTree(query?: Object) {
return request({
url: '/param/queryTree',
method: 'get',
params: query,
});
}
// 查询某个参数
export function queryOneService(yq: string,xxdh: string) {
return request({
url: '/param/queryOne',
method: 'get',
params: {yq,xxdh}
});
}

View File

@ -1,7 +1,5 @@
import request from '@/utils/request'
import {
parseStrEmpty
} from "@/utils/ruoyi";
import { parseStrEmpty } from "@/utils/ruoyi";
// 查询用户列表
export function listUser(query) {
@ -135,20 +133,3 @@ export function deptTreeSelect() {
method: 'get'
})
}
// 从老lis系统导入
export function importFromLis() {
return request({
url: '/system/user/syncUser',
method: 'get'
})
}
// 获取登录用户姓名
export function getUserName(data) {
return request({
url: '/system/role/getUserName',
method: 'get',
params: data
})
}

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M436.288 512L144.064 219.84a53.696 53.696 0 0 1 75.776-75.968l292.48 292.48 292.224-292.48a53.696 53.696 0 1 1 75.968 75.968L588.288 512.064l292.224 292.416a53.696 53.696 0 0 1-75.968 75.776L512.256 588.032l-292.416 292.224a53.44 53.44 0 0 1-75.776 0 53.44 53.44 0 0 1 0-75.776L436.288 512z" fill="#FC2530" /></svg>

Before

Width:  |  Height:  |  Size: 582 B

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="171.52px" viewBox="0 0 1194 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M1135.018667 99.84c-29.525333-22.442667-61.952-18.773333-84.992 10.24L469.162667 705.792 151.552 418.56l-5.632-6.229333c-24.064-23.04-56.234667-20.906667-83.370667 0-29.525333 22.698667-23.296 52.906667 0 81.578666l306.005334 394.24 7.168 8.277334a138.24 138.24 0 0 0 183.637333 13.653333c8.618667-6.656 16.384-14.336 23.210667-22.869333l547.669333-704.170667 4.778667-6.826667c17.237333-28.501333 27.477333-55.466667 0-76.202666V99.84z" fill="#009269" /></svg>

Before

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 473 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/images/pay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -97,19 +97,3 @@
font-size: 14px;
border-radius: 4px;
}
.btn_green {
background-color: #aef5ef !important;
color: #148A7F;
border-color: #aef5ef;
}
.btns_box {
padding: .3125rem .625rem !important;
font-size: .875rem;
height: 1.25rem !important;
margin: 5px 0;
margin-left: .625rem !important;
border-radius: 0 !important;
}

View File

@ -123,10 +123,7 @@ aside {
//main-container全局样式
.app-container {
padding: 10px 10px 0 10px;
height: calc(100vh - 5.25rem);
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.07);
background-color: #f9f9f9;
padding: 20px;
}
.components-container {
@ -135,7 +132,7 @@ aside {
}
.pagination-container {
margin-top: 10px;
margin-top: 30px;
}
.text-center {
@ -176,32 +173,6 @@ aside {
}
}
.compact-form {
flex: 1;
position: relative;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
.el-form-item {
margin-bottom: 0px;
}
.el-form-item__label {
padding-bottom: 0px;
color: #08355E;
font-size: .75rem;
}
// pointer-events: none;
:deep(.el-radio__label) {
color: #08355E;
}
}
.filter-container {
padding-bottom: 10px;
@ -211,306 +182,3 @@ aside {
margin-bottom: 10px;
}
}
.flex-between {
display: flex;
justify-content: space-between;
}
.time-line-box {
display: flex;
justify-content: space-around;
align-items: center;
margin-top: .35rem;
background-color: #f0f3f8;
padding: .3rem 2.5rem;
.time-line-item {
display: flex;
align-items: center;
justify-content: space-around;
width: 16.6%;
.timeline-content {
display: flex;
flex-direction: column;
align-items: center;
.index {
width: 1.875rem;
height: 1.875rem;
line-height: 1.875rem;
background: #E1E9F7;
border-radius: 50%;
text-align: center;
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 500;
font-size: 1.125rem;
color: #4767A3;
}
.active {
background-color: #4096ff;
box-shadow: 0 0 0 .25rem rgba(64, 150, 255, 0.2);
color: #FFF;
}
.timeline-title {
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 500;
font-size: 1rem;
color: #4767A3;
}
.timeline-time {
font-family: SourceHanSansCN, SourceHanSansCN;
font-weight: 400;
font-size: .875rem;
color: #4767A3;
height: 1rem;
}
}
.no_active {
.index,
.timeline-title {
color: #9b9999 !important;
}
}
.timg {
width: 3.125rem;
}
}
}
.next_box {
display: flex;
align-items: center;
justify-content: space-between;
.jt_btn {
width: 1.5rem;
height: 2rem;
line-height: 2rem;
font-size: 1rem;
text-align: center;
background-color: #1f6dd3;
color: #fff;
cursor: pointer;
}
.next_btn {
margin-right: 0.2rem;
}
}
.search-form {
:deep(.el-form-item__label) {
padding: 0 !important;
}
:deep(.el-form-item) {
margin-right: 0;
}
.el-form-item--default {
margin-bottom: .3125rem !important;
}
.el-form-item--small {
margin-bottom: .3125rem !important;
}
}
.page-box {
margin-top: 10px;
float: right;
}
.tabs_box {
.el-tabs__header {
height: 1.875rem !important;
margin-bottom: 0px;
font-size: .875rem;
.el-tabs__item {
height: 1.875rem !important;
padding: 0 .625rem;
&.is-active {
border-bottom: .125rem solid #409eff !important;
background-color: #e9f1fb;
}
}
}
.el-tabs__nav-next,
.el-tabs__nav-prev {
line-height: 1.875rem;
color: #1f6dd3;
}
}
.dialog-footer {
margin-top: 20px;
text-align: right;
}
.el-tree-node__children {
padding-left: 15px !important;
}
/* 禁止文本选中 */
.no-select {
user-select: none;
/* 兼容旧版浏览器 */
-webkit-user-select: none;
/* Safari/Chrome */
-moz-user-select: none;
/* Firefox */
-ms-user-select: none;
/* IE/Edge */
cursor: pointer;
}
.query-form {
margin-bottom: 5px;
border-bottom: 1px solid #ebeef5;
.el-form-item {
margin-bottom: 10px;
}
}
.bb_table {
.vxe-table.border--full {
border: 1px solid #ccc !important;
.vxe-header--column,
.vxe-body--column,
.vxe-footer--column {
border-right: 1px solid #ccc !important;
border-bottom: 1px solid #ccc !important;
height: 30px;
line-height: 30px;
padding: 0 !important;
background-image: none !important;
color: #000;
}
.vxe-footer--column {
border-bottom: none !important;
}
/* 表头最下方单元格强制底红线 */
.vxe-header--row:last-of-type .vxe-header--column {
border-bottom: 1px solid #ccc !important;
}
.vxe-body--row {
border-bottom: 1px solid #ccc !important;
}
.vxe-footer--row.vxe-footer--row {
border-top: 1px solid #ccc !important;
}
.vxe-header--row th {
height: 30px !important;
line-height: 30px;
padding: 0 !important;
}
.vxe-body--row td {
height: 30px !important;
line-height: 30px;
padding: 0 !important;
}
.vxe-footer--row td {
height: 30px !important;
line-height: 30px;
font-size: 13px;
font-weight: 700;
padding: 0 !important;
}
/* 清除最后一列重复竖线,避免双线重叠 */
.vxe-header--col:last-child .vxe-header--column,
.vxe-body--col:last-child .vxe-body--column,
.vxe-footer--col:last-child .vxe-footer--column {
border-right: none !important;
}
}
.vxe-table--render-default .vxe-table--footer-wrapper {
border-top: 1px solid #ccc !important;
}
.vxe-table--header-wrapper {
border-bottom: 1px solid #ccc !important;
background-color: #eff6fa !important;
}
}
.el-input.is-disabled .el-input__wrapper {
background-color: #dee1e5 !important;
}
.el-select__wrapper.is-disabled {
background-color: #dee1e5 !important;
}
.el-select__wrapper.is-disabled .el-select__selected-item {
color: #606266 !important;
}
.el-date-editor.is-disabled,
.el-date-editor.is-disabled .el-input__wrapper {
background-color: #dee1e5 !important;
}
.el-radio__input.is-disabled+span.el-radio__label,
.el-checkbox.is-disabled {
color: #606266 !important;
}
.el-radio__input.is-disabled.is-checked .el-radio__inner {
background: #1f6dd3;
border-color: #1f6dd3;
&::after {
background: #dee1e5;
}
}
.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner {
background: #00a5ff;
&::after {
border-color: #fff;
}
}
.el-checkbox__input.is-disabled+span.el-checkbox__label {
color: #606266 !important;
}

View File

@ -4,624 +4,260 @@
*/
/** 基础通用 **/
.pt5 {
padding-top: .3125rem;
}
.pt5 {
padding-top: 5px;
}
.pr5 {
padding-right: 5px;
}
.pb5 {
padding-bottom: 5px;
}
.mt5 {
margin-top: 5px;
}
.mr5 {
margin-right: 5px;
}
.mb5 {
margin-bottom: 5px;
}
.mb8 {
margin-bottom: 8px;
}
.ml5 {
margin-left: 5px;
}
.mt10 {
margin-top: 10px;
}
.mr10 {
margin-right: 10px;
}
.mb10 {
margin-bottom: 10px;
}
.ml10 {
margin-left: 10px;
}
.mt20 {
margin-top: 20px;
}
.mr20 {
margin-right: 20px;
}
.mb20 {
margin-bottom: 20px;
}
.ml20 {
margin-left: 20px;
}
.pr5 {
padding-right: .3125rem;
}
.pb5 {
padding-bottom: .3125rem;
}
.mt5 {
margin-top: .3125rem;
}
.mr5 {
margin-right: .3125rem;
}
.mb5 {
margin-bottom: .3125rem;
}
.mb8 {
margin-bottom: .5rem;
}
.ml5 {
margin-left: .3125rem;
}
.mt10 {
margin-top: .625rem;
}
.mr10 {
margin-right: .625rem;
}
.mb10 {
margin-bottom: .625rem;
}
.ml10 {
margin-left: .625rem;
}
.mt20 {
margin-top: 1.25rem;
}
.mr20 {
margin-right: 1.25rem;
}
.mb20 {
margin-bottom: 1.25rem;
}
.ml20 {
margin-left: 1.25rem;
}
.cp {
cursor: pointer;
}
.h1,
.h2,
.h3,
.h4,
.h5,
.h6,
h1,
h2,
h3,
h4,
h5,
h6 {
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
font-family: inherit;
font-weight: 500;
line-height: 1.1;
color: inherit;
}
}
.el-form--inline .el-form-item {
margin-right: .9375rem;
}
.el-form-item__label {
// font-weight: 700;
// font-size: 14px;
color: #08355E;
}
.el-dialog:not(.is-fullscreen) {
.el-form .el-form-item__label {
font-weight: 700;
}
.el-dialog:not(.is-fullscreen) {
margin-top: 6vh !important;
padding: 0;
// border-radius: 20px;
}
.el-dialog__body {
padding: 10px;
}
}
.el-dialog.scrollbar .el-dialog__body {
.el-dialog.scrollbar .el-dialog__body {
overflow: auto;
overflow-x: hidden;
max-height: 70vh;
padding: 16px;
}
padding: 10px 20px 0;
}
.el-dialog__header {
background: linear-gradient(90deg, #0c1a97 0%, #1373d3 100%);
padding: 10px 20px;
position: relative;
font-family: SourceHanSansCN, SourceHanSansCN;
&::before {
content: '';
position: absolute;
left: 20px;
top: 50%;
transform: translateY(-50%);
width: 28px;
height: 28px;
background: url('@/assets/logo/logo.png') no-repeat center;
background-size: contain;
z-index: 1;
}
// 标题文本样式
.el-dialog__title {
color: #fff;
/* 标题文字颜色 */
padding-left: 40px;
/* 给 Logo 留出空间 */
font-size: 16px;
font-weight: 600;
}
.el-dialog__headerbtn .el-dialog__close {
color: #fff;
/* 关闭按钮颜色 */
font-size: 18px;
&:hover {
color: #fff;
}
}
}
.el-dialog__footer {
padding: 10px;
}
.el-table {
padding: 0 !important;
border-radius: .625rem !important;
.el-table__header-wrapper,
.el-table__fixed-header-wrapper {
.el-table {
.el-table__header-wrapper, .el-table__fixed-header-wrapper {
th {
word-break: break-word;
background-color: #1F6DD3 !important;
color: #FFF;
height: 1.875rem !important;
font-size: .8125rem;
background-color: #f8f8f9 !important;
color: #515a6e;
height: 40px !important;
font-size: 13px;
}
}
.el-table__body-wrapper {
.el-button [class*="el-icon-"]+span {
.el-button [class*="el-icon-"] + span {
margin-left: 1px;
}
}
.el-table__body td {
font-size: .8125rem;
color: #000;
}
/* 表格最外层边框(可选) */
.el-table--border {
border: 1px solid #1F6DD3 !important;
}
.el-scrollbar__bar.is-vertical {
right: 0;
width: 9px !important;
}
.el-scrollbar__bar.is-horizontal {
height: 8px !important;
bottom: 0;
}
.el-scrollbar__thumb {
background-color: #817e7e !important;
opacity: 1;
}
.el-scrollbar__thumb:hover {
background-color: #817e7e !important;
opacity: 1;
}
.cell.el-tooltip {
min-width: 0;
}
.el-table__body-wrapper .el-table-column--selection>.cell {
padding: 0 !important;
height: 1.8125rem;
line-height: 1.8125rem;
}
.el-table--enable-row-hover .el-table__body tr:hover>td.el-table__cell {
background-color: #a4beef !important;
color: #000 !important;
}
.cell.el-tooltip {
padding: 0 !important;
height: 1.8125rem;
line-height: 1.8125rem;
}
.el-table-header-cell .cell {
padding: 0 !important;
}
.el-table--default {
.cell {
padding: 0 !important;
}
}
.el-table__cell {
padding: 0 !important;
height: 1.8125rem;
line-height: 1.8125rem;
}
.el-checkbox {
height: 100% !important;
}
/* 修改选中行背景色 */
.el-table__body tr.current-row>td {
background-color: #a4beef !important;
}
// /* 可选:修改悬停颜色 */
// .el-table__body tr.hover-row>td {
// background-color: #a4beef !important;
// }
.el-table__empty-block {
width: 100% !important;
}
}
.el-table--border {
border: 1px solid #96B8D9 !important;
border-top: none !important;
}
.el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #96B8D9 !important;
}
.el-table--border .el-table__cell {
border-right: 1px solid #96B8D9 !important;
}
/* 表格内部边框(可选) */
.el-table--border th.el-table__cell {
border-right: 1px solid #1F6DD3 !important;
}
.el-input__wrapper,
.el-select__wrapper {
box-shadow: 0 0 0 1px #96B8D9 inset;
.is-focus {
box-shadow: 0 0 0 1px #1f6dd3 inset !important;
}
&:hover {
box-shadow: 0 0 0 1px #1f6dd3 inset !important;
}
}
.el-select__wrapper.is-disabled {
background-color: #dfdcdc !important;
}
.el-select-dropdown__item.is-hovering {
background-color: #a4beef !important;
}
.el-input.is-disabled .el-input__wrapper {
background-color: #dfdcdc !important;
}
.el-input.is-disabled .el-input__inner {
-webkit-text-fill-color: #4e4c4c;
}
.el-date-editor.el-input__wrapper {
box-shadow: 0 0 0 1px #96B8D9 inset;
.is-focus {
box-shadow: 0 0 0 1px #1f6dd3 inset !important;
}
&:hover {
box-shadow: 0 0 0 1px #1f6dd3 inset !important;
}
}
.mytable-style {
.vxe-table {
border: 1px solid #96B8D9 !important;
border-top: none !important;
/* 蓝色外边框 */
border-radius: .5rem;
/* 可选:添加边框圆角 */
overflow: hidden;
}
.vxe-header--column {
word-break: break-word;
background-color: #1F6DD3 !important;
color: #FFF;
height: 1.875rem !important;
line-height: 1.875rem !important;
padding: 0 !important;
font-size: .8125rem;
border-color: #1F6DD3 !important;
}
.vxe-table--render-default.is--padding .vxe-body--column .vxe-cell {
padding: 0 !important;
}
.vxe-table--render-default .vxe-header--column .vxe-cell {
padding: 0 !important;
}
.vxe-table--render-default .vxe-body--column,
.vxe-footer--column,
.col--ellipsis {
height: 1.875rem !important;
line-height: 1.875rem !important;
color: #000;
}
.el-checkbox {
height: 1.875rem !important;
}
}
// 高亮
.vxe-table--render-default .vxe-body--row.row--current {
background-color: #a4beef !important;
color: #FFF !important;
}
.vxe-table--render-default .vxe-body--row.row--current .vxe-cell {
background-color: #a4beef !important;
color: #FFF !important;
}
// 滚动条
.vxe-table ::-webkit-scrollbar {
width: .6rem;
height: .5rem;
}
.vxe-table ::-webkit-scrollbar-thumb {
background-color: #817e7e;
cursor: pointer;
}
.vxe-table ::-webkit-scrollbar-thumb:hover {
background-color: #817e7e;
cursor: pointer;
}
.el-table--default .cell {
padding: 0 0 0 .3125rem !important;
}
// 表格input宽度,表格select宽度
.full-width-input .el-input__inner .el-select__inner {
width: 100%;
height: 100%;
background: transparent !important;
border: none !important;
}
.full-width-input {
.el-input__wrapper {
// box-shadow: 0 0 0 1px rgba(64, 158, 255, 0.2);
box-shadow: none;
background: transparent !important;
padding: 0 !important;
padding-left: .3125rem !important;
outline: none;
&:hover {
box-shadow: none !important;
}
}
.el-select__wrapper {
box-shadow: none;
background: transparent !important;
padding: 0 !important;
padding-left: .3125rem !important;
outline: none;
&:hover {
box-shadow: none !important;
}
}
}
/** 表单布局 **/
.form-header {
font-size: .9375rem;
color: #6379bb;
border-bottom: 1px solid #ddd;
margin: .5rem .625rem 1.5625rem .625rem;
padding-bottom: 5px
}
/** 表格布局 **/
.pagination-container {
}
/** 表单布局 **/
.form-header {
font-size:15px;
color:#6379bb;
border-bottom:1px solid #ddd;
margin:8px 10px 25px 10px;
padding-bottom:5px
}
/** 表格布局 **/
.pagination-container {
position: relative;
height: 50px;
height: 25px;
margin-bottom: 10px;
margin-top: 15px;
padding: 10px 20px !important;
}
}
.el-dialog .pagination-container {
.el-dialog .pagination-container {
position: static !important;
}
}
/* tree border */
.tree-border {
/* tree border */
.tree-border {
margin-top: 5px;
border: 1px solid #e5e6e7;
background: #FFFFFF none;
border-radius: 4px;
border-radius:4px;
width: 100%;
}
}
.pagination-container .el-pagination {
.pagination-container .el-pagination {
right: 0;
position: absolute;
}
}
@media (max-width : 768px) {
.pagination-container .el-pagination>.el-pagination__jump {
@media ( max-width : 768px) {
.pagination-container .el-pagination > .el-pagination__jump {
display: none !important;
}
.pagination-container .el-pagination>.el-pagination__sizes {
.pagination-container .el-pagination > .el-pagination__sizes {
display: none !important;
}
}
}
.el-table .fixed-width .el-button--small {
.el-table .fixed-width .el-button--small {
padding-left: 0;
padding-right: 0;
width: inherit;
}
}
/** 表格更多操作下拉样式 */
.el-table .el-dropdown-link {
/** 表格更多操作下拉样式 */
.el-table .el-dropdown-link {
cursor: pointer;
color: #1F6DD3;
color: #409EFF;
margin-left: 10px;
}
}
.el-table .el-dropdown,
.el-icon-arrow-down {
.el-table .el-dropdown, .el-icon-arrow-down {
font-size: 12px;
}
}
.el-tree-node__content>.el-checkbox {
.el-tree-node__content > .el-checkbox {
margin-right: 8px;
}
}
.list-group-striped>.list-group-item {
.list-group-striped > .list-group-item {
border-left: 0;
border-right: 0;
border-radius: 0;
padding-left: 0;
padding-right: 0;
}
}
.list-group {
.list-group {
padding-left: 0px;
list-style: none;
}
}
.list-group-item {
.list-group-item {
border-bottom: 1px solid #e7eaec;
border-top: 1px solid #e7eaec;
margin-bottom: -1px;
padding: 11px 0px;
font-size: 13px;
}
}
.pull-right {
.pull-right {
float: right !important;
}
}
.el-card__header {
.el-card__header {
padding: 14px 15px 7px !important;
min-height: 40px;
}
}
.el-card__body {
.el-card__body {
padding: 15px 20px 20px 20px !important;
}
}
.card-box {
.card-box {
padding-right: 15px;
padding-left: 15px;
margin-bottom: 10px;
}
}
/* button color */
.el-button--cyan.is-active,
.el-button--cyan:active {
/* button color */
.el-button--cyan.is-active,
.el-button--cyan:active {
background: #20B2AA;
border-color: #20B2AA;
color: #FFFFFF;
}
}
.el-button--cyan:focus,
.el-button--cyan:hover {
.el-button--cyan:focus,
.el-button--cyan:hover {
background: #48D1CC;
border-color: #48D1CC;
color: #FFFFFF;
}
}
.el-button--cyan {
.el-button--cyan {
background-color: #20B2AA;
border-color: #20B2AA;
color: #FFFFFF;
}
}
/* text color */
.text-navy {
/* text color */
.text-navy {
color: #1ab394;
}
}
.text-primary {
.text-primary {
color: inherit;
}
}
.text-success {
.text-success {
color: #1c84c6;
}
}
.text-info {
.text-info {
color: #23c6c8;
}
}
.text-warning {
.text-warning {
color: #f8ac59;
}
}
.text-danger {
.text-danger {
color: #ed5565;
}
}
.text-muted {
.text-muted {
color: #888888;
}
}
/* image */
.img-circle {
/* image */
.img-circle {
border-radius: 50%;
}
}
.img-lg {
.img-lg {
width: 120px;
height: 120px;
}
}
.avatar-upload-preview {
.avatar-upload-preview {
position: absolute;
top: 50%;
transform: translate(50%, -50%);
@ -630,16 +266,16 @@
border-radius: 50%;
box-shadow: 0 0 4px #ccc;
overflow: hidden;
}
}
/* 拖拽列样式 */
.sortable-ghost {
/* 拖拽列样式 */
.sortable-ghost{
opacity: .8;
color: #fff !important;
background: #42b983 !important;
}
color: #fff!important;
background: #42b983!important;
}
/* 表格右侧工具栏样式 */
.top-right-btn {
/* 表格右侧工具栏样式 */
.top-right-btn {
margin-left: auto;
}
}

View File

@ -36,7 +36,7 @@ $base-sub-menu-background:#000c17;
$base-sub-menu-hover:#001528;
*/
$--color-primary: #1F6DD3;
$--color-primary: #409EFF;
$--color-success: #67C23A;
$--color-warning: #E6A23C;
$--color-danger: #F56C6C;

87
src/auto-imports.d.ts vendored
View File

@ -1,87 +0,0 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const createPinia: typeof import('pinia')['createPinia']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const defineStore: typeof import('pinia')['defineStore']
const effectScope: typeof import('vue')['effectScope']
const getActivePinia: typeof import('pinia')['getActivePinia']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const mapActions: typeof import('pinia')['mapActions']
const mapGetters: typeof import('pinia')['mapGetters']
const mapState: typeof import('pinia')['mapState']
const mapStores: typeof import('pinia')['mapStores']
const mapWritableState: typeof import('pinia')['mapWritableState']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const setActivePinia: typeof import('pinia')['setActivePinia']
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const storeToRefs: typeof import('pinia')['storeToRefs']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useId: typeof import('vue')['useId']
const useLink: typeof import('vue-router')['useLink']
const useModel: typeof import('vue')['useModel']
const useRoute: typeof import('vue-router')['useRoute']
const useRouter: typeof import('vue-router')['useRouter']
const useSlots: typeof import('vue')['useSlots']
const useTemplateRef: typeof import('vue')['useTemplateRef']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}

View File

@ -1,511 +0,0 @@
<script setup lang="ts">
// @ts-ignore
import { Clock, Mesh, OrthographicCamera, PlaneGeometry, Scene, ShaderMaterial, Vector2, Vector3, WebGLRenderer } from 'three';
import { onBeforeUnmount, onMounted, ref, useTemplateRef, watch, type CSSProperties } from 'vue';
const vertexShader = `
precision highp float;
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;
const fragmentShader = `
precision highp float;
uniform float iTime;
uniform vec3 iResolution;
uniform float animationSpeed;
uniform bool enableTop;
uniform bool enableMiddle;
uniform bool enableBottom;
uniform int topLineCount;
uniform int middleLineCount;
uniform int bottomLineCount;
uniform float topLineDistance;
uniform float middleLineDistance;
uniform float bottomLineDistance;
uniform vec3 topWavePosition;
uniform vec3 middleWavePosition;
uniform vec3 bottomWavePosition;
uniform vec2 iMouse;
uniform bool interactive;
uniform float bendRadius;
uniform float bendStrength;
uniform float bendInfluence;
uniform bool parallax;
uniform float parallaxStrength;
uniform vec2 parallaxOffset;
uniform vec3 lineGradient[8];
uniform int lineGradientCount;
const vec3 BLACK = vec3(0.0);
const vec3 PINK = vec3(233.0, 71.0, 245.0) / 255.0;
const vec3 BLUE = vec3(47.0, 75.0, 162.0) / 255.0;
mat2 rotate(float r) {
return mat2(cos(r), sin(r), -sin(r), cos(r));
}
vec3 background_color(vec2 uv) {
vec3 col = vec3(0.0);
float y = sin(uv.x - 0.2) * 0.3 - 0.1;
float m = uv.y - y;
col += mix(BLUE, BLACK, smoothstep(0.0, 1.0, abs(m)));
col += mix(PINK, BLACK, smoothstep(0.0, 1.0, abs(m - 0.8)));
return col * 0.5;
}
vec3 getLineColor(float t, vec3 baseColor) {
if (lineGradientCount <= 0) {
return baseColor;
}
vec3 gradientColor;
if (lineGradientCount == 1) {
gradientColor = lineGradient[0];
} else {
float clampedT = clamp(t, 0.0, 0.9999);
float scaled = clampedT * float(lineGradientCount - 1);
int idx = int(floor(scaled));
float f = fract(scaled);
int idx2 = min(idx + 1, lineGradientCount - 1);
vec3 c1 = lineGradient[idx];
vec3 c2 = lineGradient[idx2];
gradientColor = mix(c1, c2, f);
}
return gradientColor * 0.5;
}
float wave(vec2 uv, float offset, vec2 screenUv, vec2 mouseUv, bool shouldBend) {
float time = iTime * animationSpeed;
float x_offset = offset;
float x_movement = time * 0.1;
float amp = sin(offset + time * 0.2) * 0.3;
float y = sin(uv.x + x_offset + x_movement) * amp;
if (shouldBend) {
vec2 d = screenUv - mouseUv;
float influence = exp(-dot(d, d) * bendRadius);
float bendOffset = (mouseUv.y - screenUv.y) * influence * bendStrength * bendInfluence;
y += bendOffset;
}
float m = uv.y - y;
return 0.0175 / max(abs(m) + 0.01, 1e-3) + 0.01;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 baseUv = (2.0 * fragCoord - iResolution.xy) / iResolution.y;
baseUv.y *= -1.0;
if (parallax) {
baseUv += parallaxOffset;
}
vec3 col = vec3(0.0);
vec3 b = lineGradientCount > 0 ? vec3(0.0) : background_color(baseUv);
vec2 mouseUv = vec2(0.0);
if (interactive) {
mouseUv = (2.0 * iMouse - iResolution.xy) / iResolution.y;
mouseUv.y *= -1.0;
}
if (enableBottom) {
for (int i = 0; i < bottomLineCount; ++i) {
float fi = float(i);
float t = fi / max(float(bottomLineCount - 1), 1.0);
vec3 lineCol = getLineColor(t, b);
float angle = bottomWavePosition.z * log(length(baseUv) + 1.0);
vec2 ruv = baseUv * rotate(angle);
col += lineCol * wave(
ruv + vec2(bottomLineDistance * fi + bottomWavePosition.x, bottomWavePosition.y),
1.5 + 0.2 * fi,
baseUv,
mouseUv,
interactive
) * 0.2;
}
}
if (enableMiddle) {
for (int i = 0; i < middleLineCount; ++i) {
float fi = float(i);
float t = fi / max(float(middleLineCount - 1), 1.0);
vec3 lineCol = getLineColor(t, b);
float angle = middleWavePosition.z * log(length(baseUv) + 1.0);
vec2 ruv = baseUv * rotate(angle);
col += lineCol * wave(
ruv + vec2(middleLineDistance * fi + middleWavePosition.x, middleWavePosition.y),
2.0 + 0.15 * fi,
baseUv,
mouseUv,
interactive
);
}
}
if (enableTop) {
for (int i = 0; i < topLineCount; ++i) {
float fi = float(i);
float t = fi / max(float(topLineCount - 1), 1.0);
vec3 lineCol = getLineColor(t, b);
float angle = topWavePosition.z * log(length(baseUv) + 1.0);
vec2 ruv = baseUv * rotate(angle);
ruv.x *= -1.0;
col += lineCol * wave(
ruv + vec2(topLineDistance * fi + topWavePosition.x, topWavePosition.y),
1.0 + 0.2 * fi,
baseUv,
mouseUv,
interactive
) * 0.1;
}
}
fragColor = vec4(col, 1.0);
}
void main() {
vec4 color = vec4(0.0);
mainImage(color, gl_FragCoord.xy);
gl_FragColor = color;
}
`;
const MAX_GRADIENT_STOPS = 8;
type WavePosition = {
x: number;
y: number;
rotate: number;
};
type FloatingLinesProps = {
linesGradient?: string[];
enabledWaves?: Array<'top' | 'middle' | 'bottom'>;
lineCount?: number | number[];
lineDistance?: number | number[];
topWavePosition?: WavePosition;
middleWavePosition?: WavePosition;
bottomWavePosition?: WavePosition;
animationSpeed?: number;
interactive?: boolean;
bendRadius?: number;
bendStrength?: number;
mouseDamping?: number;
parallax?: boolean;
parallaxStrength?: number;
mixBlendMode?: CSSProperties['mixBlendMode'];
};
const props = withDefaults(defineProps<FloatingLinesProps>(), {
enabledWaves: () => ['top', 'middle', 'bottom'],
lineCount: () => [6],
lineDistance: () => [5],
bottomWavePosition: () => ({ x: 2.0, y: -0.7, rotate: -1 }),
animationSpeed: 1,
interactive: true,
bendRadius: 5.0,
bendStrength: -0.5,
mouseDamping: 0.05,
parallax: true,
parallaxStrength: 0.2,
mixBlendMode: 'screen'
});
function hexToVec3(hex: string): Vector3 {
let value = hex.trim();
if (value.startsWith('#')) {
value = value.slice(1);
}
let r = 255;
let g = 255;
let b = 255;
if (value.length === 3) {
r = parseInt(value[0] + value[0], 16);
g = parseInt(value[1] + value[1], 16);
b = parseInt(value[2] + value[2], 16);
} else if (value.length === 6) {
r = parseInt(value.slice(0, 2), 16);
g = parseInt(value.slice(2, 4), 16);
b = parseInt(value.slice(4, 6), 16);
}
return new Vector3(r / 255, g / 255, b / 255);
}
const containerRef = useTemplateRef('containerRef');
const targetMouseRef = ref<Vector2>(new Vector2(-1000, -1000));
const currentMouseRef = ref<Vector2>(new Vector2(-1000, -1000));
const targetInfluenceRef = ref<number>(0);
const currentInfluenceRef = ref<number>(0);
const targetParallaxRef = ref<Vector2>(new Vector2(0, 0));
const currentParallaxRef = ref<Vector2>(new Vector2(0, 0));
let cleanup: (() => void) | null = null;
const setup = () => {
if (!containerRef.value) return;
const getLineCount = (waveType: 'top' | 'middle' | 'bottom'): number => {
if (typeof props.lineCount === 'number') return props.lineCount;
if (!props.enabledWaves.includes(waveType)) return 0;
const index = props.enabledWaves.indexOf(waveType);
return props.lineCount[index] ?? 6;
};
const getLineDistance = (waveType: 'top' | 'middle' | 'bottom'): number => {
if (typeof props.lineDistance === 'number') return props.lineDistance;
if (!props.enabledWaves.includes(waveType)) return 0.1;
const index = props.enabledWaves.indexOf(waveType);
return props.lineDistance[index] ?? 0.1;
};
const topLineCount = props.enabledWaves.includes('top') ? getLineCount('top') : 0;
const middleLineCount = props.enabledWaves.includes('middle') ? getLineCount('middle') : 0;
const bottomLineCount = props.enabledWaves.includes('bottom') ? getLineCount('bottom') : 0;
const topLineDistance = props.enabledWaves.includes('top') ? getLineDistance('top') * 0.01 : 0.01;
const middleLineDistance = props.enabledWaves.includes('middle') ? getLineDistance('middle') * 0.01 : 0.01;
const bottomLineDistance = props.enabledWaves.includes('bottom') ? getLineDistance('bottom') * 0.01 : 0.01;
const scene = new Scene();
const camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1);
camera.position.z = 1;
const renderer = new WebGLRenderer({ antialias: true, alpha: false });
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));
renderer.domElement.style.width = '100%';
renderer.domElement.style.height = '100%';
containerRef.value.appendChild(renderer.domElement);
const uniforms = {
iTime: { value: 0 },
iResolution: { value: new Vector3(1, 1, 1) },
animationSpeed: { value: props.animationSpeed },
enableTop: { value: props.enabledWaves.includes('top') },
enableMiddle: { value: props.enabledWaves.includes('middle') },
enableBottom: { value: props.enabledWaves.includes('bottom') },
topLineCount: { value: topLineCount },
middleLineCount: { value: middleLineCount },
bottomLineCount: { value: bottomLineCount },
topLineDistance: { value: topLineDistance },
middleLineDistance: { value: middleLineDistance },
bottomLineDistance: { value: bottomLineDistance },
topWavePosition: {
value: new Vector3(
props.topWavePosition?.x ?? 10.0,
props.topWavePosition?.y ?? 0.5,
props.topWavePosition?.rotate ?? -0.4
)
},
middleWavePosition: {
value: new Vector3(
props.middleWavePosition?.x ?? 5.0,
props.middleWavePosition?.y ?? 0.0,
props.middleWavePosition?.rotate ?? 0.2
)
},
bottomWavePosition: {
value: new Vector3(
props.bottomWavePosition?.x ?? 2.0,
props.bottomWavePosition?.y ?? -0.7,
props.bottomWavePosition?.rotate ?? 0.4
)
},
iMouse: { value: new Vector2(-1000, -1000) },
interactive: { value: props.interactive },
bendRadius: { value: props.bendRadius },
bendStrength: { value: props.bendStrength },
bendInfluence: { value: 0 },
parallax: { value: props.parallax },
parallaxStrength: { value: props.parallaxStrength },
parallaxOffset: { value: new Vector2(0, 0) },
lineGradient: {
value: Array.from({ length: MAX_GRADIENT_STOPS }, () => new Vector3(1, 1, 1))
},
lineGradientCount: { value: 0 }
};
if (props.linesGradient && props.linesGradient.length > 0) {
const stops = props.linesGradient.slice(0, MAX_GRADIENT_STOPS);
uniforms.lineGradientCount.value = stops.length;
stops.forEach((hex, i) => {
const color = hexToVec3(hex);
uniforms.lineGradient.value[i].set(color.x, color.y, color.z);
});
}
const material = new ShaderMaterial({
uniforms,
vertexShader,
fragmentShader
});
const geometry = new PlaneGeometry(2, 2);
const mesh = new Mesh(geometry, material);
scene.add(mesh);
const clock = new Clock();
const setSize = () => {
const el = containerRef.value!;
const width = el.clientWidth || 1;
const height = el.clientHeight || 1;
renderer.setSize(width, height, false);
const canvasWidth = renderer.domElement.width;
const canvasHeight = renderer.domElement.height;
uniforms.iResolution.value.set(canvasWidth, canvasHeight, 1);
};
setSize();
const ro = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(setSize) : null;
if (ro && containerRef.value) {
ro.observe(containerRef.value);
}
const handlePointerMove = (event: PointerEvent) => {
const rect = renderer.domElement.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const dpr = renderer.getPixelRatio();
targetMouseRef.value.set(x * dpr, (rect.height - y) * dpr);
targetInfluenceRef.value = 1.0;
if (props.parallax) {
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const offsetX = (x - centerX) / rect.width;
const offsetY = -(y - centerY) / rect.height;
targetParallaxRef.value.set(offsetX * props.parallaxStrength, offsetY * props.parallaxStrength);
}
};
const handlePointerLeave = () => {
targetInfluenceRef.value = 0.0;
};
if (props.interactive) {
renderer.domElement.addEventListener('pointermove', handlePointerMove);
renderer.domElement.addEventListener('pointerleave', handlePointerLeave);
}
let raf = 0;
const renderLoop = () => {
uniforms.iTime.value = clock.getElapsedTime();
if (props.interactive) {
currentMouseRef.value.lerp(targetMouseRef.value, props.mouseDamping);
uniforms.iMouse.value.copy(currentMouseRef.value);
currentInfluenceRef.value += (targetInfluenceRef.value - currentInfluenceRef.value) * props.mouseDamping;
uniforms.bendInfluence.value = currentInfluenceRef.value;
}
if (props.parallax) {
currentParallaxRef.value.lerp(targetParallaxRef.value, props.mouseDamping);
uniforms.parallaxOffset.value.copy(currentParallaxRef.value);
}
renderer.render(scene, camera);
raf = requestAnimationFrame(renderLoop);
};
renderLoop();
cleanup = () => {
cancelAnimationFrame(raf);
if (ro && containerRef.value) {
ro.disconnect();
}
if (props.interactive) {
renderer.domElement.removeEventListener('pointermove', handlePointerMove);
renderer.domElement.removeEventListener('pointerleave', handlePointerLeave);
}
geometry.dispose();
material.dispose();
renderer.dispose();
if (renderer.domElement.parentElement) {
renderer.domElement.parentElement.removeChild(renderer.domElement);
}
};
};
onMounted(() => {
setup();
});
onBeforeUnmount(() => {
cleanup?.();
});
watch(
() => [
props.linesGradient,
props.enabledWaves,
props.lineCount,
props.lineDistance,
props.topWavePosition,
props.middleWavePosition,
props.bottomWavePosition,
props.animationSpeed,
props.interactive,
props.bendRadius,
props.bendStrength,
props.mouseDamping,
props.parallax,
props.parallaxStrength
],
() => {
cleanup?.();
setup();
},
{
deep: true
}
);
</script>
<template>
<div ref="containerRef" class="relative w-full h-full overflow-hidden floating-lines-container" :style="{
mixBlendMode: mixBlendMode
}" />
</template>

View File

@ -4,10 +4,18 @@
<template v-if="values.includes(item.value)">
<span
v-if="(item.elTagType == 'default' || item.elTagType == '') && (item.elTagClass == '' || item.elTagClass == null)"
:key="item.value" :index="index" :class="item.elTagClass">{{ item.label + " " }}</span>
<el-tag v-else :disable-transitions="true" :key="item.value + ''" :index="index"
:type="item.elTagType === 'default' ? 'primary' : item.elTagType" :class="item.elTagClass">{{ item.label + " "
}}</el-tag>
:key="item.value"
:index="index"
:class="item.elTagClass"
>{{ item.label + " " }}</span>
<el-tag
v-else
:disable-transitions="true"
:key="item.value + ''"
:index="index"
:type="item.elTagType === 'primary' ? '' : item.elTagType"
:class="item.elTagClass"
>{{ item.label + " " }}</el-tag>
</template>
</template>
<template v-if="unmatch && showValue">
@ -68,7 +76,7 @@ function handleArray(array) {
</script>
<style scoped>
.el-tag+.el-tag {
.el-tag + .el-tag {
margin-left: 10px;
}
</style>

View File

@ -1,330 +0,0 @@
<template>
<div class="double-click-input-table" ref="componentRef">
<!-- 输入框 -->
<el-input ref="inputRef" v-model="inputValue" class="full-width-input" :placeholder="placeholder"
:disabled="disabled" @dblclick="handleDoubleClick" @change="handleInputBlur" />
<!-- 下拉表格面板 -->
<el-popover ref="popoverRef" v-model:visible="isPopoverVisible" :width="popoverWidth" trigger="click"
placement="bottom-start" :teleported="false" @hide="handlePopoverHide">
<!-- 搜索框 -->
<el-input ref="searchInputRef" v-model="searchKeyword" placeholder="搜索..." size="small" class="search-input"
@input="handleSearch" @keydown="handleSearchKeydown" />
<!-- 表格 -->
<el-table ref="tableRef" :data="filteredData" :height="tableHeight" highlight-current-row
:current-row-key="currentRowKey" @row-dblclick="handleRowDblClick" class="select-table">
<!-- 动态渲染列 -->
<el-table-column v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.label"
:width="column.width" :align="column.align || 'left'" />
</el-table>
</el-popover>
</div>
</template>
<script setup lang="ts">
import { ref, watch, nextTick, onMounted, defineProps, defineEmits, toRefs } from 'vue';
import { ElInput, ElPopover, ElTable, ElTableColumn } from 'element-plus';
// 定义列的类型
interface TableColumn {
prop: string;
label: string;
width?: number | string;
align?: 'left' | 'center' | 'right';
}
interface DataItem {
[key: string]: any; // 或者更严格的字段定义
}
const props = defineProps<{
modelValue: string | number | DataItem;
data: DataItem[];
columns: TableColumn[];
labelField: string;
valueField: string;
placeholder?: string;
disabled?: boolean;
popoverWidth?: number | string;
tableHeight?: number | string;
searchable?: boolean;
}>();
// 定义事件
const emits = defineEmits(['update:modelValue', 'change', 'select', 'visible-change']);
// 解构 props
const { modelValue, data, columns, labelField, valueField, searchable } = toRefs(props);
// 组件内部状态
const inputValue = ref('');
const isPopoverVisible = ref(false);
const searchKeyword = ref('');
const filteredData = ref<any[]>([]);
const currentRowKey = ref('');
const currentIndex = ref(-1);
const isInputFocused = ref(false);
// 引用
const inputRef = ref<InstanceType<typeof ElInput>>();
const popoverRef = ref<InstanceType<typeof ElPopover>>();
const tableRef = ref<InstanceType<typeof ElTable>>();
const searchInputRef = ref<InstanceType<typeof ElInput>>();
const componentRef = ref<HTMLElement | null>(null);
// 初始化
onMounted(() => {
filteredData.value = [...data.value];
syncInputValue();
// 监听数据变化
watch(data, (newVal) => {
filteredData.value = [...newVal];
handleSearch();
});
// 监听选中值变化
watch(modelValue, () => {
syncInputValue();
});
});
// 同步输入框值
const syncInputValue = () => {
if (modelValue.value && typeof modelValue.value === 'object') {
inputValue.value = modelValue.value[labelField.value] || '';
setCurrentRow(modelValue.value);
} else if (modelValue.value) {
// 当值不是对象时,查找对应的数据
const found = data.value.find((item: any) => item[valueField.value] === modelValue.value);
inputValue.value = found ? found[labelField.value] : '';
setCurrentRow(found);
} else {
inputValue.value = '';
currentRowKey.value = '';
currentIndex.value = -1;
}
};
// 设置当前选中行
const setCurrentRow = (row: any) => {
if (!row) return;
currentRowKey.value = row[valueField.value];
const index = filteredData.value.findIndex(item => item[valueField.value] === row[valueField.value]);
if (index !== -1) {
currentIndex.value = index;
}
};
// 双击输入框显示下拉面板
const handleDoubleClick = () => {
if (props.disabled) return;
isPopoverVisible.value = true;
emits('visible-change', true);
// 延迟聚焦搜索框,确保弹窗已渲染
nextTick(() => {
if (searchable.value && searchInputRef.value) {
searchInputRef.value.focus();
} else if (tableRef.value) {
// 如果不可搜索,聚焦表格
const tableEl = tableRef.value.$el.querySelector('.el-table__body-wrapper');
tableEl?.focus();
}
});
};
// 处理搜索
const handleSearch = () => {
const keyword = searchKeyword.value.toLowerCase().trim();
if (!keyword) {
filteredData.value = [...data.value];
currentIndex.value = -1;
return;
}
// 过滤数据,在所有列中搜索
filteredData.value = data.value.filter((item: any) => {
return columns.value.some(column => {
const value = item[column.prop];
return value !== null && value !== undefined &&
String(value).toLowerCase().includes(keyword);
});
});
// 重置当前索引
currentIndex.value = filteredData.value.length > 0 ? 0 : -1;
if (currentIndex.value !== -1) {
currentRowKey.value = filteredData.value[0][valueField.value];
scrollToCurrentRow();
} else {
currentRowKey.value = '';
}
};
// 表格行点击
const handleRowClick = (row: any) => {
currentRowKey.value = row[valueField.value];
currentIndex.value = filteredData.value.findIndex(item => item[valueField.value] === row[valueField.value]);
};
// 表格行双击
const handleRowDblClick = (row: any) => {
selectRow(row);
};
// 选择行
const selectRow = (row: any) => {
if (!row) return;
inputValue.value = row[labelField.value];
emits('update:modelValue', row[valueField.value]);
emits('change', row[valueField.value]);
emits('select', row);
isPopoverVisible.value = false;
emits('visible-change',);
// 聚焦输入框
nextTick(() => {
inputRef.value?.focus();
});
};
// 处理输入框失焦
const handleInputBlur = () => {
emits('visible-change');
};
// 处理输入框键盘事件
const handleInputKeydown = (e: any) => {
// 按向下箭头显示下拉面板
if (e.key === 'ArrowDown' && isInputFocused.value) {
e.preventDefault();
handleDoubleClick();
}
};
// 处理搜索框键盘事件
const handleSearchKeydown = (e: any) => {
// 上下箭头控制表格选择
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
e.preventDefault();
handleArrowKey(e.key);
}
// 回车键选择
else if (e.key === 'Enter') {
e.preventDefault();
if (currentIndex.value !== -1 && filteredData.value[currentIndex.value]) {
selectRow(filteredData.value[currentIndex.value]);
}
}
// ESC键关闭
else if (e.key === 'Escape') {
isPopoverVisible.value = false;
emits('visible-change', false);
inputRef.value?.focus();
}
};
// 处理箭头键
const handleArrowKey = (direction: 'ArrowUp' | 'ArrowDown') => {
if (filteredData.value.length === 0) return;
if (direction === 'ArrowUp') {
currentIndex.value = Math.max(0, currentIndex.value - 1);
} else {
currentIndex.value = Math.min(filteredData.value.length - 1, currentIndex.value + 1);
}
const currentRow = filteredData.value[currentIndex.value];
currentRowKey.value = currentRow[valueField.value];
scrollToCurrentRow();
};
// 滚动到当前行
const scrollToCurrentRow = () => {
nextTick(() => {
const table = tableRef.value;
if (!table) return;
const rowEl = table.$el.querySelector(`.el-table__row[current-row-key="${currentRowKey.value}"]`);
const scrollContainer = table.$el.querySelector('.el-table__body-wrapper');
if (rowEl && scrollContainer) {
// 计算滚动位置,使当前行居中
const containerHeight = scrollContainer.clientHeight;
const rowHeight = rowEl.offsetHeight;
const rowTop = rowEl.offsetTop;
const scrollTop = rowTop - containerHeight / 2 + rowHeight / 2;
scrollContainer.scrollTo({
top: scrollTop,
behavior: 'smooth'
});
}
});
};
// 处理弹窗隐藏
const handlePopoverHide = () => {
searchKeyword.value = '';
handleSearch(); // 重置搜索
emits('visible-change', false);
};
</script>
<style scoped lang="scss">
.full-width-input .el-input__inner {
width: 100%;
height: 100%;
background: transparent !important;
border: none !important;
}
.full-width-input {
:deep(.el-input__wrapper) {
// box-shadow: 0 0 0 1px rgba(64, 158, 255, 0.2);
box-shadow: none;
background: transparent !important;
padding: 0 !important;
outline: none;
&:hover {
box-shadow: none !important;
}
}
}
.double-click-input-table {
position: relative;
width: 100%;
}
.search-input {
margin-bottom: 8px;
width: 100%;
}
.select-table {
width: 100%;
}
::v-deep .el-table__body-wrapper {
tabindex: 0;
outline: none;
}
::v-deep .el-table__row.current-row {
background-color: #e6f7ff !important;
}
</style>

View File

@ -1,109 +0,0 @@
<template>
<div v-if="visible" class="print-mask">
<div class="mask-card">
<div class="card-header">
<div class="title">打印报告进度</div>
<el-button icon="Close" circle size="small" v-if="progress == 100" @click="closeMask" />
</div>
<div class="tip-text">{{ ybh }} {{ desc }}</div>
<el-progress :percentage="progress" :stroke-width="15" striped />
</div>
</div>
</template>
<script setup lang="ts">
import { classCom } from '@/utils/classCom';
const props = defineProps<{
// websocket原始data字符串
wsData?: string
}>()
const visible = ref(false)
const ybh = ref('')
const desc = ref('')
const progress = ref(0)
const succCount = ref(0)
const failCount = ref(0)
const errorMsg = ref('')
// 解析ws数据
const parseWsData = (dataStr: string) => {
try {
const data = JSON.parse(dataStr)
ybh.value = data.ybh
desc.value = data.desc
progress.value = data.progress
succCount.value = data.succCount
failCount.value = data.failCount
errorMsg.value = data.errorMsg
visible.value = true
// 100%完成后打印PDF
if (data.progress == 100) {
classCom.printBase64PDF(data.pdfBase64)
}
} catch (err) {
console.error('打印ws数据解析失败', err)
}
}
watch(
() => props.wsData,
(val) => {
if (val) parseWsData(val)
},
{ immediate: true }
)
// 对外暴露手动关闭方法
const closeMask = () => {
visible.value = false
}
defineExpose({ closeMask })
</script>
<style scoped lang="scss">
.print-mask {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.55);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
.mask-card {
background: #fff;
width: 460px;
padding: 24px;
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
// 标题行布局
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16;
.title {
font-size: 16px;
font-weight: 600;
color: #1d2129;
}
}
.tip-text {
color: #4e5969;
margin-bottom: 20px;
line-height: 1.7;
font-size: 14px;
margin-top: 20px;
}
.finish-text {
margin-top: 20px;
text-align: center;
font-size: 15px;
color: #00b42a;
font-weight: 500;
}
}
}
</style>

View File

@ -8,16 +8,14 @@
<el-button circle icon="Refresh" @click="refresh()" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns">
<el-button circle icon="Menu" @click="showColumn()" v-if="showColumnsType == 'transfer'" />
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px"
v-if="showColumnsType == 'checkbox'">
<el-button circle icon="Menu" @click="showColumn()" v-if="showColumnsType == 'transfer'"/>
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px" v-if="showColumnsType == 'checkbox'">
<el-button circle icon="Menu" />
<template #dropdown>
<el-dropdown-menu>
<template v-for="item in columns" :key="item.key">
<el-dropdown-item>
<el-checkbox :checked="item.visible" @change="checkboxChange($event, item.label)"
:label="item.label" />
<el-checkbox :checked="item.visible" @change="checkboxChange($event, item.label)" :label="item.label" />
</el-dropdown-item>
</template>
</el-dropdown-menu>
@ -26,7 +24,12 @@
</el-tooltip>
</el-row>
<el-dialog :title="title" v-model="open" append-to-body>
<el-transfer :titles="['显示', '隐藏']" v-model="value" :data="columns" @change="dataChange"></el-transfer>
<el-transfer
:titles="['显示', '隐藏']"
v-model="value"
:data="columns"
@change="dataChange"
></el-transfer>
</el-dialog>
</div>
</template>
@ -59,7 +62,7 @@ const props = defineProps({
},
})
const emits = defineEmits(['update:showSearch', 'queryTable', 'updateColumns']);
const emits = defineEmits(['update:showSearch', 'queryTable']);
// 显隐数据
const value = ref([]);
@ -111,9 +114,6 @@ if (props.showColumnsType == 'transfer') {
// 勾选
function checkboxChange(event, label) {
props.columns.filter(item => item.label == label)[0].visible = event;
emits("updateColumns", toRaw(props.columns));
}
</script>
@ -124,11 +124,9 @@ function checkboxChange(event, label) {
display: block;
margin-left: 0px;
}
:deep(.el-transfer__button:first-child) {
margin-bottom: 10px;
}
:deep(.el-dropdown-menu__item) {
line-height: 30px;
padding: 0 17px;

View File

@ -1,77 +0,0 @@
<!--仪器组件的封装-->
<template>
<SelectTable v-model:data="modelValue" :fields="fields" :tableData="instrOptions" label="yqmc" :size="props.size"
value="yq" objKey="yq" :border="true" :width="props.width" placeholder="请选择仪器" :clearable="props.clearable"
@getDataValue="getDataValue" />
</template>
<script setup lang="ts">
import SelectTable from '@/components/SelectTable/index.vue';
import { getGroupInstrdList } from '@/api/liswork/work/LisWork';
import { Emits, Props, Field } from '@/types';
import { ref, watch, onMounted } from 'vue';
const modelValue = ref<string | null>('');
const props = withDefaults(defineProps<{
width?: string | number,
size?: string,
clearable?: boolean,
instrGroup?: string | number, //部门组代号
data: string | undefined
}>(), {
width: '100%',
size: 'default',
clearable: false,
data: ''
})
const instrOptions = ref<Array<{ yq: string; yqmc: string; yqdl: string }>>([]);
const fields = ref(
[
{ prop: 'yq', label: '代号', width: 80, enablePinyinSearch: true },
{ prop: 'yqmc', label: '名称', enablePinyinSearch: true },
]
)
const emits = defineEmits<Emits>();
const getYqConfig = () => {
const data = {
lisgroup: props.instrGroup
}
getGroupInstrdList(data)
.then((res: any) => {
if (res.code == 0) {
instrOptions.value = res.data.map((item: any) => ({
yq: item.yq.trim(),
yqmc: item.yqmc,
yqdl: item.yqdl,
lisgroupid: item.lisgroup
}))
}
})
}
const getDataValue = (val: any) => {
emits('update:data', val?.yq || null);
emits('getDataValue', val || null);
};
watch(() => props.data, (val) => {
modelValue.value = val;
});
onMounted(() => {
// 初始化时同步一次父级值(如果有)到内部 modelValue
if (props.data !== undefined && props.data !== null) {
modelValue.value = props.data as any;
}
getYqConfig();
});
</script>

View File

@ -1,407 +0,0 @@
<!--
* SelectTable 下拉表格组件
* @author: w
* @since: 2025-9-28
* SelectTable.vue
*
* 属性(带有[Required]为必填属性)
* data[Required]: 双向绑定数据
* fields[Required]: 表格列对象
* tableData[Required]: 表格数据对象
* value: 双向绑定数据的值(为空时data属性为表格当前行数据对象)
* label: 选择框显示的文本内容(为空时分别查看value和objKey是否为空,若value和objKey都有值,则value优先级大于objKey)
* objKey: 对象key(绑定值的唯一标识,绑定值为对象时必填) 数据回显对应值
* placeholder: 选择框占位文本
* size: 组件大小
* border: 表格是否带有边框
-->
<template>
<div class="select-table-container">
<el-select ref="selectTable" class="select-table__wrapper" v-model="selectShowValue"
:placeholder="props.placeholder" :size="props.size" :style="{ width: props.width }"
@visible-change="visibleChange" @clear="clearHandle" :clearable="props.clearable" :disabled="disabled"
v-bind="$attrs">
<template #empty>
<div class="select-table-dropdown">
<div style="text-align: left;">
<el-input v-model="searchKey" ref="searchInput" size="small" placeholder="快速搜索(支持简拼)" class="mb10" clearable
@clear="filterDataHandle" @input="filterDataHandle" @keydown="handleInputKeydown" />
</div>
<!-- 数据为空时显示空状态提示 -->
<el-empty v-if="filterData.length === 0" description="没有匹配的数据" :image-size="100" />
<div @keydown="handleKeydown" v-else tabindex="0" style="outline: none;">
<!-- 数据存在时显示表格 -->
<!-- <el-table ref="tableRef" :data="filterData" :highlight-current-row="props.isHighlight" style="width: 100%"
:border="props.border" @row-click="handleRowChange" max-height="350px" :row-style="{ height: '25px' }">
<el-table-column v-for="field in props.fields" :prop="field.prop" :label="field.label"
:width="field.width" show-overflow-tooltip align="center" />
</el-table> -->
<vxe-table class="mytable-style" ref="tableRef" border resizable max-height="350px"
:row-config="{ isHover: true, keyField: 'value', height: 30, isCurrent: true }" :data="filterData"
:current-row="currentRow" @cell-click="currentChange"
:tooltip-config="{ appendToBody: true, zIndex: 3000 }" :show-overflow="true"
:scroll-y="{ enabled: true, rSize: 30, height: '100%', oSize: 20, gt: 30, }">
<vxe-table-column v-for="field in props.fields" :field="field.prop" :title="field.label" align="center"
:width="field.width" min-width="150" />
</vxe-table>
</div>
</div>
</template>
</el-select>
</div>
</template>
<script setup lang="ts">
import { reactive, ref, onMounted, watch, nextTick, toRaw } from "vue";
import { getFirstLetter } from '@/utils/pinyin';
import { ElEmpty } from 'element-plus';
import { comDict } from '@/utils/dict'
import { getoptionselect } from '@/api/liswork/xtwh/localconfigApi'
import { Emits, Props } from '@/types';
import { getOptionSelect } from '@/hooks'
const props = withDefaults(defineProps<Props>(), {
placeholder: "请选择",
size: "default",
isHighlight: true,
value: 'value',
label: 'label',
border: true,
width: "100%",
dictType: '',
optionselect: false,
clearable: true,
objKey: 'value',
keyValue: false,
fields: () => [
{ prop: 'value', label: '代号', width: 80, enablePinyinSearch: true },
{ prop: 'label', label: '名称', enablePinyinSearch: true },
],
});
const emits = defineEmits<Emits>();
const searchKey = ref('');
const tableRef = ref();
const selectTable = ref();
const selectShowValue = ref('');
const searchInput = ref();
const filterData = ref<{ [key: string]: any; pinyinMap: Record<string, string> }[]>([]);
// 存储预处理后的带拼音数据
const processedTableData = ref<{ [key: string]: any; pinyinMap: Record<string, string> }[]>([]);
const currentIndex = ref(-1);
const currentRow = ref<any>(null)
// 字典数据存储
const dictData = ref<Record<string, any[]>>({});
// 预处理数据:生成拼音信息
const processTableData = (data: object[]) => {
return data.map((item: any) => {
const pinyinMap: Record<string, string> = {};
// 处理label字段拼音
if (item[props.label!]) {
pinyinMap.label = getFirstLetter(item[props.label!]).toLowerCase();
}
// 处理value字段拼音
if (props.value && item[props.value]) {
pinyinMap.value = getFirstLetter(String(item[props.value])).toLowerCase();
}
// 处理其他指定字段拼音
props.fields.forEach(field => {
if (field.enablePinyinSearch && item[field.prop]) {
pinyinMap[field.prop] = getFirstLetter(item[field.prop]).toLowerCase();
}
});
return { ...item, pinyinMap };
});
};
// 隐藏显示处理
const visibleChange = (val: any) => {
searchKey.value = '';
if (val) {
filterDataHandle()
setTimeout(() => {
searchInput.value.focus();
currentIndex.value = filterData.value.findIndex(item => item[props.value!] === props.data);
if (currentIndex.value >= 0) {
tableRef.value.setCurrentRow(filterData.value[currentIndex.value])
tableRef.value.scrollToRow(filterData.value[currentIndex.value])
}
}, 10);
}
};
// 数据回显处理
const handleDataEcho = () => {
if (props.data === null || props.data === undefined) {
selectShowValue.value = '';
emits('update:data', null);
// emits('getDataValue', null);
} else {
// 查找匹配的行数据
let row: any = []
if (props.dictType) {
row = dictData.value[props.dictType]?.find((item: any) => props.objKey && item[props.objKey] === props.data)
} else if (props.tableData) {
row = props.tableData?.find((item: any) => props.objKey && item[props.objKey] === props.data)
}
selectShowValue.value = row ? (props.keyValue ? `${row[props.value]} ${row[props.label]}` : (props.label ? row[props.label] : (props.value ? row[props.value] : ''))) : props.data;
}
};
interface DictRefs {
[key: string]: {
value: any[];
};
}
// 初始化数据
const initData = async () => {
if (props.tableData) {
// 如果传入tableData,直接使用
processedTableData.value = processTableData(props.tableData);
filterData.value = [...processedTableData.value];
} else if (props.dictType) {
let dictRefs: DictRefs = {};
// 如果传入dictType,通过comDict获取数据 optionselect为true 用通用接口
if (props.optionselect) {
const resp = await getoptionselect({ zdlb: props.dictType });
const dictList = resp.data.map((p: any) => ({
label: p.zdmc,
value: p.zddh,
}));
dictRefs[props.dictType] = { value: dictList };
getOptionSelect(props.dictType, dictList);
} else {
dictRefs = await comDict(props.dictType);
}
// // 从 ref 中获取实际数据
dictData.value = {
[props.dictType]: toRaw(dictRefs[props.dictType].value) || [],
};
processedTableData.value = processTableData(dictData.value[props.dictType]);
filterData.value = [...processedTableData.value];
}
// 处理数据回显
handleDataEcho();
};
initData();
// 初始化数据
onMounted(() => {
});
// 监听表格数据和字典类型变化
watch(() => props.tableData, (newVal) => {
if (newVal) {
processedTableData.value = processTableData(newVal);
handleDataEcho();
}
}, { deep: true });
// 监听绑定值数据变化,重新处理
watch(() => props.data, (newVal) => {
// 数据回显
handleDataEcho();
}, { deep: true });
// 输入框键盘事件处理(专门处理上下键)
const handleInputKeydown = (e: KeyboardEvent) => {
// 仅处理上下方向回车键
if (!['ArrowUp', 'ArrowDown', 'Enter'].includes(e.key)) return;
// 阻止事件冒泡到输入框父元素
e.stopPropagation();
// 如果有筛选数据,转移焦点到表格容器
if (filterData.value.length > 0) {
// 筛选后让表格容器获得焦点
nextTick(() => {
// 通过组件内部的 DOM 结构关系查找当前实例的表格容器 避免找不到table焦点
const dropdown = searchInput.value.$el.closest('.select-table-dropdown');
const tableContainer = dropdown?.querySelector('div[tabindex="0"]') as HTMLElement;
if (tableContainer) {
tableContainer.focus();
}
// 手动触发一次表格容器的键盘事件
const event = new KeyboardEvent('keydown', { key: e.key });
tableContainer?.dispatchEvent(event);
});
// 回车确认
if (e.key === "Enter") {
const currentRow = filterData.value[currentIndex.value];
handleRowChange(currentRow);
}
}
};
// 监听table键盘事件(核心)
const handleKeydown = (e: any) => {
// console.log('e==>', e);
// 仅处理方向键和回车键
if (!['ArrowUp', 'ArrowDown', 'Enter'].includes(e.key)) return;
// 阻止默认行为(如页面滚动)
e.preventDefault();
// 根据方向键更新“当前选中行索引”
const maxIndex = filterData.value.length - 1; // 末行索引
if (e.key === "ArrowUp") {
// 按↑:索引减1,最小为0(首行)
currentIndex.value = Math.max(0, currentIndex.value - 1);
} else if (e.key === "ArrowDown") {
// 按↓:索引加1,最大为 maxIndex(末行)
currentIndex.value = Math.min(maxIndex, currentIndex.value + 1);
}
tableRef.value.setCurrentRow(filterData.value[currentIndex.value])
tableRef.value.scrollToRow(filterData.value[currentIndex.value])
// 回车确认
if (e.key === "Enter") {
if (searchKey.value.trim() !== '') {
// 如果输入框有内容,回车时先触发一次输入框的筛选逻辑,确保数据是最新的
filterDataHandle();
return
}
const currentRow = filterData.value[currentIndex.value];
handleRowChange(currentRow);
emits('enter-press');
}
}
// 搜索过滤逻辑
const filterDataHandle = () => {
const key = searchKey.value.trim().toLowerCase();
if (!key) {
// 空搜索时显示全部预处理数据
filterData.value = [...processedTableData.value];
return;
} else {
currentIndex.value = 0
}
filterData.value = processedTableData.value.filter((item: any) => {
// 1. 原文本匹配
const matchLabel = props.label && item[props.label]?.toString().toLowerCase().includes(key);
const matchValue = props.value && item[props.value]?.toString().toLowerCase().includes(key);
// 2. 拼音匹配
const matchLabelPinyin = props.label && item.pinyinMap.label?.includes(key);
const matchValuePinyin = props.value && item.pinyinMap.value?.includes(key);
// 3. 其他字段匹配
let matchOtherField = false;
props.fields.forEach(field => {
if (field.enablePinyinSearch) {
const textMatch = item[field.prop]?.toString().toLowerCase().includes(key);
const pinyinMatch = item.pinyinMap[field.prop]?.includes(key);
if (textMatch || pinyinMatch) {
matchOtherField = true;
}
}
});
return matchLabel || matchValue || matchLabelPinyin || matchValuePinyin || matchOtherField;
});
if (filterData.value.length > 0) {
nextTick(() => {
tableRef.value.setCurrentRow(filterData.value[0])
tableRef.value.scrollToRow(filterData.value[0])
});
}
};
const currentChange = (params: { row: any }) => {
currentRow.value = params.row
handleRowChange(params.row)
}
const handleRowChange = (val: any) => {
emits("update:data", props.value ? val[props.value] : val);
setLabel(val);
};
const setLabel = (val: any) => {
if (props.label) {
selectShowValue.value = val[props.label];
} else if (props.value) {
selectShowValue.value = val[props.value];
} else if (props.keyValue) {
selectShowValue.value = `${val[props.value]} ${val[props.label]}`;
}
if (props.objKey) {
// 返回值objKey对应的对象数据
emits('getDataValue', val);
}
selectTable.value.blur();
};
// 清空值
const clearHandle = () => {
selectShowValue.value = '';
currentRow.value = null;
emits("update:data", null);
emits('getDataValue', null);
tableRef.value?.clearCurrentRow();
};
</script>
<style scoped lang="scss">
.select-table-container {
width: 100%;
}
:deep(.vxe-header--row th) {
border-color: #1F6DD3;
background-color: #1F6DD3;
color: #fff;
}
:deep(.vxe-body--row td) {
border-color: #1F6DD3;
}
:deep(.vxe-table--row-hover) {
background-color: #1F6DD3;
}
:deep(.vxe-table--current-row) {
background-color: #1F6DD3;
}
:deep(.el-table-header-cell) {
cursor: move;
user-select: none;
}
// 表格边框
:deep(.vxe-table--render-default.border--full .vxe-body--column) {
background-image: linear-gradient(#96B8D9, #96B8D9), linear-gradient(#96B8D9, #96B8D9) !important;
}
// 表头边框
:deep(.vxe-table--render-default.border--full .vxe-header--column) {
background-image: linear-gradient(#1F6DD3, #1F6DD3), linear-gradient(#1F6DD3, #1F6DD3) !important;
}
.select-table-dropdown {
padding: 8px;
}
:deep(.el-table .el-table__cell) {
padding: 0;
}
</style>

View File

@ -1,18 +1,30 @@
<template>
<el-menu :default-active="activeMenu" mode="horizontal" @select="handleSelect" :ellipsis="false">
<el-menu
:default-active="activeMenu"
mode="horizontal"
@select="handleSelect"
:ellipsis="false"
>
<template v-for="(item, index) in topMenus">
<el-menu-item :style="{ '--theme': theme }" :index="item.path" :key="index" v-if="index < visibleNumber">
<svg-icon v-if="item.meta && item.meta.icon && item.meta.icon !== '#'" :icon-class="item.meta.icon" />
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber">
<svg-icon
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
:icon-class="item.meta.icon"/>
{{ item.meta.title }}
</el-menu-item>
</template>
<!-- 顶部菜单超出数量折叠 -->
<el-sub-menu :style="{ '--theme': theme }" index="more" v-if="topMenus.length > visibleNumber">
<el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
<template #title>更多菜单</template>
<template v-for="(item, index) in topMenus">
<el-menu-item :index="item.path" :key="index" v-if="index >= visibleNumber">
<svg-icon v-if="item.meta && item.meta.icon && item.meta.icon !== '#'" :icon-class="item.meta.icon" />
<el-menu-item
:index="item.path"
:key="index"
v-if="index >= visibleNumber">
<svg-icon
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
:icon-class="item.meta.icon"/>
{{ item.meta.title }}
</el-menu-item>
</template>
@ -67,10 +79,10 @@ const childrenMenus = computed(() => {
routers.value.map((router) => {
for (let item in router.children) {
if (router.children[item].parentPath === undefined) {
if (router.path === "/") {
if(router.path === "/") {
router.children[item].path = "/" + router.children[item].path;
} else {
if (!isHttp(router.children[item].path)) {
if(!isHttp(router.children[item].path)) {
router.children[item].path = router.path + "/" + router.children[item].path;
}
}
@ -92,7 +104,7 @@ const activeMenu = computed(() => {
if (!route.meta.link) {
appStore.toggleSideBarHide(false);
}
} else if (!route.children) {
} else if(!route.children) {
activePath = path;
appStore.toggleSideBarHide(true);
}
@ -137,7 +149,7 @@ function activeRoutes(key) {
}
});
}
if (routes.length > 0) {
if(routes.length > 0) {
permissionStore.setSidebarRouters(routes);
} else {
appStore.toggleSideBarHide(true);
@ -158,7 +170,7 @@ onMounted(() => {
</script>
<style lang="scss">
.topmenu-container.el-menu--horizontal>.el-menu-item {
.topmenu-container.el-menu--horizontal > .el-menu-item {
float: left;
height: 50px !important;
line-height: 50px !important;
@ -167,14 +179,13 @@ onMounted(() => {
margin: 0 10px !important;
}
.topmenu-container.el-menu--horizontal>.el-menu-item.is-active,
.el-menu--horizontal>.el-sub-menu.is-active .el-submenu__title {
.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title {
border-bottom: 2px solid #{'var(--theme)'} !important;
color: #303133;
}
/* sub-menu item */
.topmenu-container.el-menu--horizontal>.el-sub-menu .el-sub-menu__title {
.topmenu-container.el-menu--horizontal > .el-sub-menu .el-sub-menu__title {
float: left;
height: 50px !important;
line-height: 50px !important;
@ -184,9 +195,7 @@ onMounted(() => {
}
/* 背景色隐藏 */
.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus,
.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover,
.topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover {
.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover {
background-color: #ffffff !important;
}

View File

@ -1,204 +0,0 @@
<template>
<div class="context-menu-wrapper" @contextmenu.prevent="handleContextMenu($event)">
<!-- 插槽:包裹需要绑定右键菜单的内容 -->
<slot />
<!-- 右键菜单 -->
<div v-if="visible" class="hospital-menu" :style="{
top: `${top}px`,
left: `${left}px`,
display: visible ? 'block' : 'none',
width: `${menuWidth}px`
}" @click.stop @contextmenu.prevent>
<template v-for="(item, index) in items" :key="index">
<div v-if="item.type === 'divider'" class="menu-divider"></div>
<div v-if="item.type !== 'divider'" class="menu-item" :class="{
danger: item.danger,
disabled: item.disabled
}" @click="handleItemClick(item)">
<span class="menu-shortcut" v-if="item.shortcut">{{ item.shortcut }}</span>
<span class="menu-label">{{ item.label }}</span>
</div>
</template>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed } from 'vue';
import { ContextMenuItem } from '@/types/index';
import emitter from '@/utils/mitt';
// 组件Props
const props = withDefaults(defineProps<{
items: ContextMenuItem[]; // 菜单项
autoClose?: boolean; // 是否自动关闭菜单
menuWidth?: number | string; // 菜单宽度
}>(), {
autoClose: true,
menuWidth: 180,
});
// 组件Emits
const emit = defineEmits<{
(e: 'select', item: ContextMenuItem): void;
(e: 'open'): void;
(e: 'close'): void;
}>();
// 菜单状态
const visible = ref(false);
const top = ref(0);
const left = ref(0);
const handleContextMenu = async (e: MouseEvent) => {
emitter.emit('closeAllContextMenus');
// 先计算初始位置
left.value = Math.min(e.clientX, window.innerWidth - (typeof props.menuWidth === 'number' ? props.menuWidth : 180));
top.value = e.clientY;
visible.value = true;
emit('open');
// 等待菜单渲染完成后获取实际高度
await nextTick();
const menuEl = document.querySelector('.hospital-menu') as HTMLElement;
if (menuEl) {
const menuHeight = menuEl.offsetHeight;
const maxTop = window.innerHeight - menuHeight;
// 确保菜单不会超出底部可视区域
if (top.value > maxTop) {
top.value = maxTop;
// 如果向上调整后仍超出顶部,则取最小0值
if (top.value < 0) top.value = 0;
}
}
};
// 处理菜单项点击
const handleItemClick = (item: ContextMenuItem) => {
if (item.disabled) return;
emit('select', item);
if (props.autoClose !== false) {
hideMenu();
}
};
// 隐藏菜单
const hideMenu = () => {
visible.value = false;
emit('close');
};
// 点击外部区域关闭菜单
const handleClickOutside = (e: MouseEvent) => {
hideMenu();
};
// 键盘事件处理
const handleKeyDown = (e: KeyboardEvent) => {
if (!visible.value) return;
if (e.key === 'Escape') {
hideMenu();
}
// 统一转换小写
const row: ContextMenuItem | undefined = props.items.find(item => item.shortcut?.toLowerCase() === e.key?.toLowerCase());
if (row && !row.disabled) {
handleItemClick(row)
}
};
onMounted(() => {
// 关闭所有菜单
emitter.on('closeAllContextMenus', () => {
if (visible.value) {
hideMenu();
}
});
document.addEventListener('click', handleClickOutside);
document.addEventListener('keydown', handleKeyDown);
});
// 卸载时移除事件监听
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside);
document.removeEventListener('keydown', handleKeyDown);
emitter.off('closeAllContextMenus');
});
</script>
<style scoped>
.context-menu-wrapper {
position: relative;
height: 100%;
}
.hospital-menu {
position: fixed;
background-color: #fff;
border: 1px solid #dcdfe6;
border-radius: .25rem;
box-shadow: 0 .125rem .75rem rgba(0, 0, 0, 0.1);
z-index: 9999;
padding: .3125rem 0;
font-size: .8125rem;
font-family: SourceHanSansCN, SourceHanSansCN;
}
.menu-item {
padding: .3125rem .625rem;
cursor: pointer;
display: flex;
align-items: center;
transition: background-color 0.2s;
/* justify-content: space-between; */
}
.menu-item:hover:not(.disabled) {
background-color: #e6f7ff;
color: #165DFF;
}
.menu-item.disabled {
color: #c0c4cc;
cursor: not-allowed;
}
.menu-item i {
margin-right: .5rem;
font-size: .875rem;
width: 1rem;
text-align: center;
}
.menu-divider {
height: 1px;
background-color: #e5e7eb;
margin: .25rem 0;
}
.menu-item.danger {
color: #f53f3f;
}
.menu-item.danger:hover:not(.disabled) {
background-color: #fff1f0;
color: #f53f3f;
}
.menu-shortcut {
color: #909399;
margin-right: .3125rem;
}
</style>

View File

@ -1,28 +0,0 @@
<template>
<span>{{ showNum }}</span>
</template>
<script setup lang="ts">
import { ref, watch, onMounted } from 'vue'
const props = defineProps<{ value: number }>()
const showNum = ref(0)
watch(
() => props.value,
(val) => {
let start = 0
const step = val / 30
const timer = setInterval(() => {
start += step
if (start >= val) {
showNum.value = val
clearInterval(timer)
} else {
showNum.value = Math.floor(start)
}
}, 30)
},
{ immediate: true }
)
</script>

View File

@ -1,482 +0,0 @@
<template>
<div class="custom-table-wrapper">
<!-- 表格主体 -->
<el-table ref="tableRef" :data="tableData" v-loading="loading" :stripe="config.stripe || false"
:border="config.border" :fit="config.fit !== true" :show-header="config.showHeader !== false"
:highlight-current-row="config.highlightCurrentRow || false" :row-class-name="config.rowClassName"
:header-cell-style="config.headerCellStyle" :cell-style="config.cellStyle" :max-height="config.maxHeight"
:show-summary="config.summary" :summary-method="getSummaries" :sum-text="config.sumText" :height="config.height"
:row-key="config.key" :class="{ 'drag-table': enableRowDrag }" :size="config.size || 'default'"
:empty-text="config.emptyText || '暂无数据'" @selection-change="handleSelectionChange"
@current-change="handleCurrentChange" @row-click="handleRowClick"
:header-cell-class-name="enableColumnDrag ? 'el-table-header-cell' : ''" @row-dblclick="handleRowDblclick"
@sort-change="handleSortChange" v-bind="$attrs">
<!-- 动态列 -->
<template v-for="item in columns" :key="item.prop">
<!-- 多选列 -->
<el-table-column v-if="item.type == 'selection' && item.visible" type="selection" :width="item.width || 55"
:fixed="item.fixed" :align="item.align || 'left'" />
<!-- 序号列 -->
<el-table-column v-if="item.type == 'index' && item.visible" type="index" class-name="el-table-column--index"
:label="item.label || '序号'" :width="item.width || 60" :fixed="item.fixed" :align="item.align || 'left'"
:index="getIndex" />
<!-- 自定义插槽列 行数据插槽(有row属性) -->
<el-table-column v-if="!item.type && item.slot && item.visible" :prop="item.prop" :label="item.label"
:width="item.width" :min-width="item.minWidth" :fixed="item.fixed" :align="item.align || 'left'"
:sortable="item.sortable" :show-overflow-tooltip="item.showOverflowTooltip !== false">
<template v-if="item.headerSlot" #header="scope">
<slot :name="item.headerSlot" :column="scope.column" :$index="scope.$index" />
</template>
<template v-if="item.slot" #default="scope">
<slot :name="item.slot" :row="scope.row" :$index="scope.$index" />
</template>
</el-table-column>
<!-- 普通列 -->
<el-table-column v-if="!item.type && !item.slot && item.visible" :prop="item.prop" :label="item.label"
:width="item.width" :min-width="item.minWidth" :fixed="item.fixed" :align="item.align || 'left'"
:sortable="item.sortable" :show-overflow-tooltip="item.showOverflowTooltip !== false"
:formatter="item.formatter">
<!-- 表头插槽 (无row属性)-->
<template v-if="item.headerSlot" #header="scope">
<slot :name="item.headerSlot" :column="scope.column" :$index="scope.$index" />
</template>
</el-table-column>
</template>
<!-- 操作列 -->
<el-table-column v-if="$slots.action" :label="config.actionLabel || '操作'" :width="config.actionWidth"
:min-width="config.actionMinWidth || 120" :fixed="config.actionFixed || 'right'"
:align="config.actionAlign || 'center'">
<template #default="scope">
<slot name="action" :row="scope.row" :column="scope.column" :$index="scope.$index" />
</template>
</el-table-column>
<!-- 空数据插槽 -->
<template v-if="$slots.empty" #empty>
<slot name="empty" />
</template>
</el-table>
<!-- 分页组件 -->
<div v-if="pagination.show" class="pagination-wrapper">
<el-pagination v-model:current-page="currentPage" v-model:page-size="currentPageSize" :total="pagination.total"
:size="aotuSize" :page-sizes="pagination.pageSizes || [10, 20, 50, 100]"
:layout="pagination.layout || 'total, sizes, prev, pager, next, jumper'"
:background="pagination.background !== false" :small="pagination.small || false"
:disabled="pagination.disabled || false" @size-change="handleSizeChange"
@current-change="handleCurrentPageChange" />
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, watch, PropType, onMounted } from "vue";
import { TableColumnCtx } from 'element-plus';
import { classCom } from "@/utils/classCom";
const aotuSize = classCom.useAutoSize();
// @ts-ignore
import Sortable from 'sortablejs'; // 引入sortablejs
interface TableColumn {
prop?: string;
type?: string; // 'selection' | 'index' | undefined
key?: string | number;
label?: string;
width?: string | number;
minWidth?: string | number;
fixed?: boolean | string;
align?: string;
sortable?: boolean;
showOverflowTooltip?: boolean;
formatter?: Function;
slot?: string;
headerSlot?: string;
visible?: boolean; // 是否显示列
}
// 在子组件中定义插槽类型
defineSlots<{
// 具名插槽和动态插槽(不含 default)
[slotName: string]: (props: { row?: any; column?: any; $index?: number }) => any;
}>();
// Props 定义
const props = defineProps({
// 表格数据
data: {
type: Array,
default: () => [],
},
// 表格列配置
columns: {
type: Array as PropType<TableColumn[]>,
default: () => [],
},
// 表格配置
config: {
type: Object,
default: () => ({}),
},
// 加载状态
loading: {
type: Boolean,
default: false,
},
// 分页配置
pagination: {
type: Object,
default: () => ({
show: false,
total: 0,
pageSize: 10,
currentPage: 1,
}),
},
// 拖拽列
enableColumnDrag: {
type: Boolean,
default: false
},
// 拖拽行
enableRowDrag: {
type: Boolean,
default: false
}
});
// Emits 定义
const emit = defineEmits([
"selection-change",
"current-change",
"row-click",
"row-dblclick",
"sort-change",
"page-change",
"size-change",
"column-drag-end",
"row-drag-end"
]);
// 响应式数据
const tableRef = ref();
const currentPage = ref(props.pagination.currentPage || 1); // 第几页
const currentPageSize = ref(props.pagination.pageSize || 10); // 一页显示多少条
// 计算属性
const tableData = computed(() => {
if (props.pagination.show && props.pagination.clientPaging) {
// 客户端分页
const start = (currentPage.value - 1) * currentPageSize.value;
const end = start + currentPageSize.value;
return props.data.slice(start, end);
}
return props.data;
});
// 序号计算
const getIndex = (index: number) => {
if (props.pagination.show && !props.pagination.clientPaging) {
return (currentPage.value - 1) * currentPageSize.value + index + 1;
}
return index + 1;
};
// const headerCellClassName = (column: TableColumnCtx<any>) => {
// if (!column.column.property) {
// return ''; // 不添加类名
// } else {
// return 'el-table-header-cell'; // 添加自定义类名
// }
// };
// 计算列合计
// 自定义汇总方法 - 只计算指定列
const getSummaries = ({ columns, data }: { columns: any, data: any }) => {
const summaries = columns.map(() => '');
if (props.config.summaryField.length > 0) {
props.config.summaryField.forEach((v: any) => {
const index = columns.find((column: any) => column.property === v)?.no;
const summaryData = data.map((item: any) => item[v]);
const summary = summaryData.reduce((acc: any, cur: any) => Number(acc) + Number(cur), 0);
summaries[index] = summary;
});
summaries[0] = props.config.sumText;
}
return summaries;
};
// 事件处理
const handleSelectionChange = (selection: any[]) => {
emit("selection-change", selection);
};
const handleCurrentChange = (currentRow: any, oldCurrentRow: any) => {
emit("current-change", currentRow, oldCurrentRow);
};
const handleRowClick = (row: any, column: TableColumnCtx<any>, event: Event) => {
emit("row-click", row, column, event);
};
const handleRowDblclick = (row: any, column: TableColumnCtx<any>, event: Event) => {
emit("row-dblclick", row, column, event);
};
const handleSortChange = (sortInfo: Object) => {
emit("sort-change", sortInfo);
};
const handleSizeChange = (size: number) => {
currentPageSize.value = size;
currentPage.value = 1;
emit("size-change", {
pageSize: size,
currentPage: 1,
});
};
const handleCurrentPageChange = (page: number) => {
currentPage.value = page;
emit("page-change", {
currentPage: page,
pageSize: currentPageSize.value,
});
};
// 初始化列拖拽功能
onMounted(() => {
if (props.enableColumnDrag && tableRef.value) {
initColumnDrag();
}
if (props.enableRowDrag && tableRef.value) {
initRowDrag();
}
});
// 监听分页配置变化
watch(
() => props.pagination.currentPage,
(newVal) => {
if (newVal) {
currentPage.value = newVal;
}
},
);
watch(
() => props.pagination.pageSize,
(newVal) => {
if (newVal) {
currentPageSize.value = newVal;
}
},
);
// 初始化列拖拽
const initColumnDrag = () => {
// 获取表头单元格
const tableHeader = tableRef.value.$el.querySelector('.el-table__header-wrapper thead tr');
if (!tableHeader) return;
// 初始化拖拽
const sortable = new Sortable(tableHeader, {
animation: 150, // 动画时间
handle: '.el-table-header-cell', // 拖拽手柄
ghostClass: 'sortable-ghost', // 拖拽时的占位符样式
// filter: '.el-table-column--selection, .el-table-column--index', // 排除选择列和序号列
onEnd: (evt: any) => {
// console.log('evt==>', evt);
// 排除固定列和选择列、序号列
const visibleColumns = props.columns.filter(col => col.visible !== false);
// 过滤掉固定列
const draggableColumns = visibleColumns.filter(col => !col.fixed);
// 如果发生了位置变化
if (evt.oldIndex !== evt.newIndex && draggableColumns.length) {
// 复制原数组并调整顺序
const newColumns = [...draggableColumns];
const [movedItem] = newColumns.splice(evt.oldIndex, 1);
newColumns.splice(evt.newIndex, 0, movedItem);
// 更新原始columns数组中对应列的位置
const updatedColumns = [...props.columns];
// 先移除所有可拖拽列
const nonDraggableColumns = updatedColumns.filter(col => col.fixed);
// 合并固定列和新顺序的可拖拽列
const resultColumns = nonDraggableColumns.concat(newColumns);
// 触发事件,通知父组件列顺序已改变
emit('column-drag-end', {
oldIndex: evt.oldIndex,
newIndex: evt.newIndex,
columns: resultColumns
});
}
}
});
};
// 初始化行拖拽
const initRowDrag = () => {
const tableBody = tableRef.value.$el.querySelector('.el-table__body-wrapper tbody');
const sortable = new Sortable(tableBody, {
animation: 150, // 拖拽动画时长
ghostClass: 'sortable-ghost', // 拖拽占位符样式
chosenClass: 'sortable-chosen', // 选中行样式
// 拖拽结束回调
onEnd: (evt: any) => {
const { oldIndex, newIndex } = evt
if (oldIndex === newIndex) return // 未改变位置则不处理
// 调整数组顺序
const moveRow = tableData.value.splice(oldIndex, 1)[0]
tableData.value.splice(newIndex, 0, moveRow)
// 重新更新排序值
tableData.value.forEach((item: any, index) => {
item.sort = index + 1
})
// console.log("🚀 ~ initRowDrag ~ tableData.value:", tableData.value)
emit('row-drag-end', {
oldIndex,
newIndex,
list: tableData.value
})
}
})
};
// 暴露方法
const clearSelection = () => {
tableRef.value?.clearSelection();
};
const toggleRowSelection = (row: Object, selected: boolean) => {
tableRef.value?.toggleRowSelection(row, selected);
};
const getSelectionRows = () => {
return tableRef.value?.getSelectionRows() || [];
};
const toggleAllSelection = () => {
tableRef.value?.toggleAllSelection();
};
const setCurrentRow = (row: Object) => {
tableRef.value?.setCurrentRow(row);
};
const setScrollTo = (top: number) => {
tableRef.value?.scrollTo(top);
};
const setScrollTop = (index: number) => {
tableRef.value?.setScrollTop(index * 30); //30px/行
};
const clearSort = () => {
tableRef.value?.clearSort();
};
const doLayout = () => {
tableRef.value?.doLayout();
};
const sort = (prop: string, order: any) => {
tableRef.value?.sort(prop, order);
};
// 导出方法
defineExpose({
clearSelection,
toggleRowSelection,
getSelectionRows,
toggleAllSelection,
setCurrentRow,
setScrollTo,
setScrollTop,
clearSort,
doLayout,
sort,
tableRef,
initColumnDrag
});
</script>
<style scoped lang="scss">
.custom-table-wrapper {
height: 100%;
}
// 只在拖拽手柄上显示移动光标
:deep(.el-table-header-cell .cell) {
cursor: move;
/* 恢复默认光标 */
}
// 拖拽手柄样式(使用排序图标作为拖拽手柄)
:deep(.el-table-header-cell .sort-caret) {
// cursor: move;
/* 只在排序图标处显示移动光标 */
// margin-left: 1px;
}
// 保留列宽调整的光标样式
:deep(.el-table__header-wrapper .el-table__header tr th .el-table__header-drag-icon) {
cursor: col-resize !important;
}
:deep(.el-table__footer-wrapper tfoot td.el-table__cell) {
color: #1f6dd3 !important;
font-weight: 700;
/* 可替换为任意颜色值 */
}
.pagination-wrapper {
display: flex;
align-items: center;
justify-content: flex-end;
padding: .3125rem 0;
margin-right: 20px;
}
// /* 修改选中行背景色 */
// ::v-deep .el-table__body tr.current-row>td {
// background-color: #a4beef !important;
// }
// /* 可选:修改悬停颜色 */
// ::v-deep .el-table__body tr.hover-row>td {
// background-color: #a4beef !important;
// }
/* 响应式设计 */
@media (max-width: 768px) {
.pagination-wrapper {
justify-content: center;
}
.pagination-wrapper :deep(.el-pagination) {
flex-wrap: wrap;
}
}
</style>

View File

@ -1,6 +1,10 @@
<template>
<div v-loading="loading" :style="'height:' + height">
<iframe :src="url" frameborder="no" style="width: 100%; height: 100%" scrolling="auto" />
<iframe
:src="url"
frameborder="no"
style="width: 100%; height: 100%"
scrolling="auto" />
</div>
</template>

View File

@ -1,165 +0,0 @@
<template>
<div class="dict-input-wrapper">
<!-- 字典选择弹窗 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="30vw" :close-on-click-modal="false" :draggable="true"
@close="handleDialogClose">
<el-input ref="inputRef" v-model="searchKey" placeholder="搜索(支持名称、编码、简拼)..." class="mb10" clearable
@clear="filterDictData" @input="filterDictData" />
<el-table :data="filteredDictData" height="300px" border @row-dblclick="selectItem" highlight-current-row>
<el-table-column prop="value" label="编码" align="center" width="150" />
<el-table-column prop="label" label="名称" align="center" show-overflow-tooltip />
<el-table-column prop="pinyin" label="简拼" align="center" width="100" /> <!-- 显示简拼便于调试 -->
</el-table>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue';
import { queryXmInfo } from '@/api/liswork/work/LisWork'
import { getFirstLetter } from '@/utils/pinyin'; // 引入拼音处理工具
// 组件参数
const props = defineProps({
modelValue: { type: [String, Number], default: '' },
dictType: { type: String },
placeholder: { type: String, default: '请输入或双击选择' },
clearable: { type: Boolean, default: true },
disabled: { type: Boolean, default: false },
dialogTitle: { type: String, default: '选择字典项' },
tableKey: { type: Object, default: () => { } },
});
// 组件事件
const emit = defineEmits(['select']);
const dictData = ref([]); // 存储带简拼的字典数据
const filteredDictData = ref([]);
const isDictLoading = ref(false);
const dialogVisible = ref(false);
const searchKey = ref('');
// 加载字典数据(并添加简拼字段)
const loadDictData = async () => {
try {
isDictLoading.value = true;
const requestParams = {
pageNum: 1,
pageSize: 1000, // 加载足够多的项目
yq: props.tableKey.yq, // 当前选中的仪器
};
const response = await queryXmInfo(requestParams);
const rawData = response.data.map((item: any, index: number) => {
return {
value: item.xmdh || `未知编码_${index}`, // 确保value存在
label: item.xmmc || `未知名称_${index}`, // 确保label存在
dw: item.dw || "",
refs: item.refs || "",
};
});
// 为每个字典项添加简拼字段
dictData.value = (rawData || []).map((item: any) => ({
...item,
pinyin: getFirstLetter(item.label) // 新增pinyin字段存储简拼
}));
filteredDictData.value = [...dictData.value];
} catch (error) {
console.error(`加载${props.dictType}字典失败:`, error);
dictData.value = [];
filteredDictData.value = [];
} finally {
isDictLoading.value = false;
}
};
// 核心搜索逻辑(支持模糊搜索+简拼搜索)
const filterDictData = () => {
const key = searchKey.value.trim().toLowerCase();
if (!key) {
filteredDictData.value = [...dictData.value];
return;
}
filteredDictData.value = dictData.value.filter((item: any) => {
// 1. 模糊搜索:匹配label(名称)或value(编码)
const matchLabel = item.label.toLowerCase().includes(key);
const matchValue = String(item.value).toLowerCase().includes(key);
// 2. 简拼搜索:匹配首字母简拼
const matchPinyin = item.pinyin.toLowerCase().includes(key);
// 满足任一条件即匹配
return matchLabel || matchValue || matchPinyin;
});
};
const inputRef = ref()
// 关键修改:打开弹窗时重新加载数据
const openDictSelector = async () => {
if (props.disabled || isDictLoading.value) {
return;
}
// 打开弹窗前先加载数据(确保每次打开都是最新的)
await loadDictData();
// 数据加载完成后再显示弹窗
dialogVisible.value = true;
searchKey.value = '';
nextTick(() => {
setTimeout(() => {
inputRef.value.focus();
}, 100);
});
filterDictData();
};
// 选择字典项
const selectItem = (item: any) => {
emit('select', item);
dialogVisible.value = false;
};
const handleDialogClose = () => {
dialogVisible.value = false;
};
defineExpose({
openDictSelector, // 暴露打开弹窗的方法
// 可选:暴露其他可能需要的方法(如刷新字典数据)
loadDictData
});
</script>
<style scoped>
/* 样式保持不变 */
.dict-input-wrapper {
position: relative;
}
.select-icon {
cursor: pointer;
color: #666;
margin-left: 5px;
}
.select-icon:hover {
color: #409eff;
}
:deep(.el-input__suffix) {
gap: 4px;
}
</style>

View File

@ -1,108 +0,0 @@
<template>
<div>
<el-dialog :title="dialogTitle" v-model="csjgVisible" width="300px" :close-on-click-modal="false" :draggable="true">
<el-input ref="inputRef" v-model="searchKey" :placeholder="placeholder" size="small" class="mb10" clearable
@clear="filterDictData" @input="filterDictData" />
<el-table :data="filterData" max-height="300px" border @row-dblclick="selectItem" highlight-current-row
:row-style="{ helght: '25px' }">
<el-table-column prop="qz" label="取值" align="center" />
<el-table-column prop="pinyin" label="简拼" align="center" />
</el-table>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, watch, computed, reactive, toRaw } from 'vue';
import { getFirstLetter } from '@/utils/pinyin';
interface Props {
tableData: object[];
label?: string;
dialogTitle?: string;
placeholder?: string;
}
const props = withDefaults(defineProps<Props>(), {
label: undefined,
dialogTitle: '选择数据',
placeholder: '支持简拼搜索'
});
const csjgVisible = ref(false)
const searchKey = ref('')
const filterData = ref<{ [key: string]: any; pinyinMap: Record<string, string> }[]>([]);
const filterDictData = () => {
const key = searchKey.value.trim().toLowerCase();
if (!key) {
// 空搜索时显示全部预处理数据
filterData.value = [...processedTableData.value];
return;
}
filterData.value = processedTableData.value.filter((item: any) => {
// 1. 原文本匹配
const matchLabel = props.label && item[props.label]?.toString().toLowerCase().includes(key);
return matchLabel;
});
}
interface Emits {
(e: "selectItem", val: any): void;
}
const emits = defineEmits<Emits>();
const selectItem = (row: any) => {
emits('selectItem', row);
csjgVisible.value = false;
}
// 存储预处理后的带拼音数据
const processedTableData = ref<{ [key: string]: any; pinyinMap: Record<string, string> }[]>([]);
// 初始化数据
onMounted(() => {
processedTableData.value = processTableData(props.tableData);
filterData.value = [...processedTableData.value];
});
// 监听表格数据变化,重新处理
watch(() => props.tableData, (newVal) => {
processedTableData.value = processTableData(newVal);
filterData.value = [...processedTableData.value];
// 数据回显
}, { deep: true });
// 预处理数据:生成拼音信息
const processTableData = (data: object[]) => {
return data.map((item: any) => {
const pinyinMap: Record<string, string> = {};
// 处理label字段拼音
if (props.label) {
pinyinMap['pinyin'] = getFirstLetter(item[props.label]).toLowerCase();
return { ...item, ...pinyinMap };
}
});
};
const inputRef = ref()
const open = () => {
csjgVisible.value = true;
nextTick(() => {
searchKey.value = '';
setTimeout(() => {
inputRef.value.focus();
}, 100);
});
};
defineExpose({
open,
});
</script>
<style scoped lang="scss">
:deep(.el-table .el-table__cell) {
padding: 0;
}
</style>

View File

@ -1,274 +0,0 @@
<template>
<el-popover :visible="selectVisible" placement="bottom" :width="'18vw'" @after-enter="initClickOutside">
<el-form :model="queryParams" class="query-form" label-width="5.625rem">
<!-- 病人类型选择 -->
<el-form-item label="病人类型:" prop="brly">
<el-select v-model="queryParams.brly" placeholder="全部" clearable>
<el-option v-for="item in brlyOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="完成状态:" prop="finish">
<el-radio-group v-model="queryParams.finish" @change="handleRadioChange">
<el-radio value="1" @click.native="handleRadioClick('1', 'finish')">已完成</el-radio>
<el-radio value="0" @click.native="handleRadioClick('0', 'finish')">未完成</el-radio>
</el-radio-group>
</el-form-item>
<!-- 审核状态选择 -->
<el-form-item label="审核状态:" prop="jgbz">
<el-radio-group v-model="queryParams.jgbz" @change="handleRadioChange">
<el-radio value="2" @click.native="handleRadioClick('2', 'jgbz')">已审核</el-radio>
<el-radio value="0" @click.native="handleRadioClick('0', 'jgbz')">未审核</el-radio>
</el-radio-group>
</el-form-item>
<el-row>
<el-col :span="8">
<!-- 自审状态选择 -->
<el-form-item label-width="0.625" prop="autojgbz">
<el-checkbox v-model="queryParams.autojgbz" true-value="1" false-value=""> 自审 </el-checkbox>
</el-form-item>
</el-col>
<el-col :span="8">
<!-- 危急值选择 -->
<el-form-item prop="alarmflag" label-width="0">
<el-checkbox v-model="queryParams.alarmflag" true-value="1" false-value=""> 危急值 </el-checkbox>
</el-form-item>
</el-col>
<el-col :span="8">
<!-- 急诊选择 -->
<el-form-item prop="jzbz" label-width="0">
<el-checkbox v-model="queryParams.jzbz" true-value="1" false-value=""> 急诊 </el-checkbox>
</el-form-item>
</el-col>
</el-row>
<!-- 打印状态选择 -->
<el-form-item label="打印状态:" prop="dybz">
<el-radio-group v-model="queryParams.dybz" @chnage="handleRadioChange">
<el-radio value="1" @click.native="handleRadioClick('1', 'dybz')">已打印</el-radio>
<el-radio value="0" @click.native="handleRadioClick('0', 'dybz')">未打印</el-radio>
</el-radio-group>
</el-form-item>
<!-- 操作按钮 -->
<el-form-item>
<el-button type="primary" @click="handleQuery" :size="props.size">确认查询</el-button>
<el-button @click="handleReset" style="margin-left: 0.5rem" :size="props.size">重置</el-button>
</el-form-item>
</el-form>
<template #reference>
<el-input v-model="selectShowValue" :size="props.size" @clear="clearHandle"
@click="selectVisible = !selectVisible" :placeholder="props.placeholder" readonly
:style="{ width: props.width }" suffix-icon="ArrowDown" :clearable="props.clearable" />
</template>
</el-popover>
</template>
<script setup lang="ts">
import { ref, nextTick, watch } from 'vue'
import { ElMessage } from 'element-plus'
// 定义下拉选项类型
interface OptionItem {
label: string
value: string | number
}
// 定义查询参数类型
interface QueryParams {
brly?: string | number
jgbz?: string | number
dybz?: string | number
alarmflag?: string | number
jzbz?: string | number
finish?: string | number
autojgbz?: string | number
}
// 定义组件Props
const props = defineProps<{
// 可传入初始查询参数
initialParams?: Partial<QueryParams>,
// 宽度
width?: string,
// 是否禁用
disabled?: boolean,
// 是否可清空
clearable?: boolean,
// 是否显示查询按钮
showQueryButton?: boolean,
// 是否显示重置按钮
showResetButton?: boolean,
// 尺寸
size?: 'large' | 'default' | 'small',
// 占位符
placeholder?: string,
dictData?: any
}>()
watch(() => props.dictData, () => {
brlyOptions.value = props.dictData.PT
optionMaps.brly = props.dictData.PT
})
// 定义组件事件
const emit = defineEmits<{
// 查询事件,返回查询参数
(e: 'query', params: QueryParams): void
// 重置事件
(e: 'reset'): void
}>()
const selectVisible = ref(false)
const selectShowValue = ref('')
const selectRef = ref()
// 病人类型选项
const brlyOptions = ref<OptionItem[]>([])
// 审核状态选项
const jgbzOptions: OptionItem[] = [
{ label: '未审核', value: 0 },
{ label: '已审核', value: 2 },
]
// 打印状态选项
const dybzOptions: OptionItem[] = [
{ label: '未打印', value: 0 },
{ label: '已打印', value: 1 },
]
// 危急值选项
const alarmflagOptions: OptionItem[] = [
{ label: '危急值', value: 1 },
{ label: '无', value: 0 }
]
// 急诊选项
const emergencyOptions: OptionItem[] = [
{ label: '急诊', value: 1 },
{ label: '否', value: 0 }
]
// 完成状态选项
const finishOptions: OptionItem[] = [
{ label: '未完成', value: 0 },
{ label: '已完成', value: 1 },
]
// 自审状态选项
const autojgbzOptions: OptionItem[] = [
{ label: '否', value: 0 },
{ label: '自审', value: 1 },
]
// 创建选项映射关系,方便查找label
const optionMaps = {
brly: props.dictData.PT,
jgbz: jgbzOptions,
dybz: dybzOptions,
alarmflag: alarmflagOptions,
jzbz: emergencyOptions,
finish: finishOptions,
autojgbz: autojgbzOptions
}
// 查询参数
const queryParams = ref<QueryParams>({
...props.initialParams
})
// 由于更多按钮在 app 元素内,给 app 元素注册的点击事件必须在弹出框显示后并且是一次性的
// 防止更多按钮的事件与之冲突
const initClickOutside = () => {
document.getElementById('app')!.addEventListener('click', () => {
selectVisible.value = false
}, { once: true })
}
// 记录上一次选中的值
const lastRadioValue = ref<string | undefined>(undefined);
const handleRadioChange = (value: string) => {
lastRadioValue.value = value;
};
const handleRadioClick = (value: string, zd: keyof QueryParams) => {
if (value === lastRadioValue.value) {
setTimeout(() => {
queryParams.value[zd] = undefined;
lastRadioValue.value = undefined;
}, 100);
} else {
lastRadioValue.value = value;
}
};
const clearHandle = () => {
queryParams.value = {}
selectShowValue.value = ''
}
// 根据值和选项列表获取对应的label
const getLabelByValue = (value: string | number | undefined, options: OptionItem[]) => {
if (value === undefined || value === null || value === '') return ''
const item = options.find(option => option.value == value)
return item ? item.label : ''
}
// 处理查询
const handleQuery = () => {
// 过滤空值参数
const filteredParams = Object.fromEntries(
Object.entries(queryParams.value).filter(([_, v]) => v !== undefined && v !== null && v !== '')
) as QueryParams
// 收集所有选中项的label
const labels: string[] = []
Object.entries(filteredParams).forEach(([key, value]) => {
// @ts-ignore
const options = optionMaps[key]
if (options) {
const label = getLabelByValue(value, options)
if (label) labels.push(label)
}
})
// 拼接label并设置到显示值
selectShowValue.value = labels.join(',')
console.log('selectShowValue.value==>', selectShowValue.value);
emit('query', filteredParams)
selectVisible.value = false;
}
// 处理重置
const handleReset = () => {
selectShowValue.value = ''
queryParams.value = {}
selectVisible.value = false;
emit('reset')
}
</script>
<style scoped lang="scss">
.query-form {
:deep(.el-form-item) {
margin-bottom: .5rem;
}
}
/* 响应式调整 */
@media (max-width: 768px) {
.query-form {
:deep(.el-form-item) {
margin-bottom: 12px;
width: 100%;
}
}
}
</style>

View File

@ -1,189 +0,0 @@
<template>
<div class="tab-container">
<el-icon class="scroll-btn left" @click="scrollAndSelect('left')" :disabled="!hasLeftScroll">
<ArrowLeft />
</el-icon>
<div class="tab-wrapper">
<div class="tab-list" ref="tabListRef" @scroll="handleScroll">
<div :class="['text', activeTab === item.value ? 'active' : '']" v-for="item in tabList" :key="item.value"
@click="handleTabClick(item.value)" ref="tabItems">
{{ item.label }}
</div>
</div>
</div>
<el-icon class="scroll-btn right" @click="scrollAndSelect('right')" :disabled="!hasRightScroll">
<ArrowRight />
</el-icon>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch } from 'vue';
import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue';
// 定义 tabList 元素的类型
interface TabItem {
label: string;
value: string | number;
}
const props = defineProps<{
tabList: TabItem[];
activeTab: string | number;
}>();
const emit = defineEmits(['update:activeTab']);
const tabListRef = ref();
const tabItems = ref<HTMLDivElement[]>([]);
const hasLeftScroll = ref(false);
const hasRightScroll = ref(false);
const handleScroll = () => {
if (tabListRef.value) {
const { scrollLeft, scrollWidth, clientWidth } = tabListRef.value;
hasLeftScroll.value = scrollLeft > 5;
hasRightScroll.value = scrollLeft < scrollWidth - clientWidth - 5;
}
};
const handleTabClick = (value: any) => {
emit('update:activeTab', value);
scrollToActiveTab();
};
// 滚动到当前激活的Tab
const scrollToActiveTab = () => {
if (!tabListRef.value) return;
const activeIndex = props.tabList.findIndex((item: any) => item.value === props.activeTab);
if (activeIndex === -1) return;
const activeTabElement = tabItems.value[activeIndex];
if (activeTabElement) {
const scrollContainer = tabListRef.value;
const containerRect = scrollContainer.getBoundingClientRect();
const tabRect = activeTabElement.getBoundingClientRect();
// 如果Tab不在可见区域内,则滚动到可见区域
if (tabRect.left < containerRect.left || tabRect.right > containerRect.right) {
scrollContainer.scrollTo({
left: tabRect.left - containerRect.left + scrollContainer.scrollLeft - 200,
behavior: 'smooth'
});
}
}
};
// 滚动并选中
const scrollAndSelect = (direction: 'left' | 'right') => {
if (!tabListRef.value || tabItems.value.length === 0) return;
const currentIndex = props.tabList.findIndex((item: any) => item.value === props.activeTab);
let newIndex;
if (direction === 'left') {
// 向左滚动并选中前一个Tab
newIndex = currentIndex > 0 ? currentIndex - 1 : 0;
} else {
// 向右滚动并选中后一个Tab
newIndex = currentIndex < props.tabList.length - 1 ? currentIndex + 1 : props.tabList.length - 1;
}
emit('update:activeTab', props.tabList[newIndex].value);
};
// 监听activeTab变化,确保滚动到可视区域
watch(() => props.activeTab, scrollToActiveTab);
// 初始化时检查滚动状态
setTimeout(() => {
handleScroll();
}, 0);
</script>
<style scoped lang="scss">
.tab-container {
position: relative;
display: flex;
align-items: center;
width: 100%;
}
.scroll-btn {
width: 1.875rem;
height: 2.9375rem;
display: flex;
align-items: center;
justify-content: center;
background: #fff;
cursor: pointer;
z-index: 10;
box-shadow: 0 0 .25rem rgba(0, 0, 0, 0.1);
transition: opacity 0.3s;
&.left {
position: absolute;
left: 0;
top: 0;
border-radius: .75rem 0 0 .75rem;
border: 1px solid #fff;
}
&.right {
position: absolute;
right: 0;
top: 0;
border-radius: 0 .75rem .75rem 0;
border: 1px solid #fff;
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
.tab-wrapper {
flex: 1;
overflow: hidden;
padding: 0 1.875rem; // 给箭头留出空间
}
.tab-list {
display: flex;
gap: 1px;
white-space: nowrap;
margin-bottom: 1px;
background-color: #fff;
cursor: pointer;
overflow-x: auto;
scrollbar-width: none; // 隐藏滚动条
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none; // 隐藏滚动条
}
}
.text {
font-weight: 400;
font-size: 1rem;
color: #1F6DD3;
width: 5.625rem;
height: 2.9375rem;
line-height: 2.9375rem;
text-align: center;
flex-shrink: 0;
cursor: pointer;
}
.active {
background: #e9f1fb;
color: #1F6DD3;
font-weight: 600;
font-family: SourceHanSansCN, SourceHanSansCN;
}
</style>

View File

@ -1,113 +0,0 @@
<template>
<div>
<el-dialog v-model="visible" :title="title" :width="500" :close-on-click-modal="false" :draggable="true">
<el-form ref="formRef" :model="formData" label-width="100px">
<!-- 用户代号 -->
<el-form-item label="用户代号:" prop="yhdh" :rules="[{ required: true, message: '请输入用户代号', trigger: 'change' }]">
<el-input ref="yhdhRef" v-model="formData.yhdh" @change="handleChange" />
</el-form-item>
<!-- 用户姓名 -->
<el-form-item label="用户姓名:">
<el-input v-model="formData.userName" disabled />
</el-form-item>
<!-- 密码输入 -->
<el-form-item label="密码:" prop="mm" :rules="[{ required: true, message: '请输入密码', trigger: 'change' }]">
<el-input v-model="formData.mm" type="password" @keyup.enter="handleConfirm" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer" style="text-align: center;">
<el-button type="primary" @click="handleConfirm"> 确定 </el-button>
<el-button @click="cancel"> 取消 </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { checkYsUser } from '@/api/liswork/work/LisWork'
interface UserInfo {
usenName: string,
nickName: string,
}
const props = defineProps({
labPatKey: {
type: Object,
default: () => { }
},
userList: {
type: Array as PropType<UserInfo[]>,
default: () => []
},
title: {
type: String,
default: '请输入锁定人员账号密码'
}
});
const visible = ref(false);
// 表单数据
const formData = ref({
yhdh: '',
userName: '',
mm: '',
})
const formRef = ref();
const yhdhRef = ref();
const open = () => {
visible.value = true
nextTick(() => {
setTimeout(() => {
yhdhRef.value?.focus();
}, 100);
});
}
const emit = defineEmits(["confirm"]);
// 处理确定按钮点击
const handleConfirm = async () => {
if (!formRef.value) return;
// 表单验证
const valid = await formRef.value.validate();
if (valid) {
checkYsUser({ ...formData.value, ...props.labPatKey }).then((res: any) => {
if (res.code == 0) {
emit('confirm', formData.value)
}
})
}
};
// 处理取消按钮点击
const cancel = () => {
formData.value = {
yhdh: '',
userName: '',
mm: '',
}
formRef.value?.resetFields();
visible.value = false
};
const handleChange = (value: string) => {
formData.value.userName = props.userList.find((item: any) => item.userName == value)?.nickName || value
};
defineExpose({
open,
cancel
})
</script>
<style scoped></style>

View File

@ -1,311 +0,0 @@
<!--
* vxe-table 通用表格组件 适用数据量大
* @props columns: 表格列配置
* @props tableData: 表格数据
* @props loading: 加载状态
* @props rowConfig: 行配置
* @props showOverflow: 是否显示内容溢出省略
* @props highlightCurrentRow: 是否高亮当前行
* @props scrollYConfig: 纵向滚动配置
* scroll-y 纵向滚动 设置虚拟滚动
-->
<template>
<div class="common-table-container">
<vxe-table :data="tableData" :loading="loading" border :checkbox-config="config.checkboxConfig"
:height="scrollYConfig.height" @checkbox-change="handleCheckboxChange" :row-config="rowConfig"
:show-overflow="showOverflow" @current-change="handleRowClick" ref="tableRef" :size="size"
:class="{ 'drag-table': enableRowDrag }" :current-row="currentRow" :scroll-y="scrollYConfig"
:tooltip-config="{ appendToBody: true, zIndex: 3000 }"
:header-cell-class-name="enableColumnDrag ? 'el-table-header-cell' : ''" v-bind="$attrs">
<!-- 动态渲染列 -->
<template v-for="(column, i) in columns" :key="`${column.field}-${i}`">
<vxe-column type="checkbox" width="40" align="center" v-if="column.type == 'selection'" />
<vxe-column width="40" align="center" :title="column.title || '序号'" v-if="column.type == 'seq'">
<template #default="scope">
{{ scope.row._index }}
</template>
</vxe-column>
<vxe-column v-bind="column" v-if="column.field">
<!-- 自定义列模板 -->
<template v-if="column.slotName" #default="scope">
<slot :name="column.slotName" :row="scope.row" :$index="scope.$rowIndex"></slot>
</template>
</vxe-column>
</template>
</vxe-table>
</div>
</template>
<script setup lang="ts">
import { ref, nextTick, watch, onMounted } from 'vue'
import Sortable from 'sortablejs'; // 引入sortablejs
import type { VxeComponentSizeType, VxeColumnPropTypes } from 'vxe-table'
// 定义列配置类型
interface TableColumn {
field: string
title: string
width?: number | string
align?: string,
slotName?: string // 自定义插槽名称
[key: string]: any // 支持其他vxe-column属性
}
// 定义props类型
const props = defineProps({
// 表格数据
tableData: {
type: Array,
default: () => []
},
// 加载状态
loading: {
type: Boolean,
default: false
},
// 列配置
columns: {
type: Array as () => TableColumn[],
required: true
},
// 行配置
rowConfig: {
type: Object,
default: () => ({
isHover: true, height: 30, isCurrent: true, keyField: '_index'
})
},
// 表格配置
config: {
type: Object,
default: () => ({}),
},
// 是否显示内容溢出省略
showOverflow: {
type: Boolean,
default: true
},
// 是否高亮当前行 已废弃
highlightCurrentRow: {
type: Boolean,
default: true
},
// 纵向滚动配置
scrollYConfig: {
type: Object,
default: () => ({
enabled: true, // 强制启用虚拟滚动
rSize: 30, // 行高(需与实际行高一致)
height: 600, // 固定表格高度(关键,单位px)
})
},
// 表格尺寸
size: {
type: String as () => VxeComponentSizeType,
default: 'medium'
},
// 拖拽列
enableColumnDrag: {
type: Boolean,
default: false
},
// 拖拽行
enableRowDrag: {
type: Boolean,
default: false
}
})
const emits = defineEmits(['current-change', "column-drag-end", "row-drag-end", "selection-change"])
const tableRef = ref<any>(null)
const currentRow = ref<any>(null)
// 处理行点击
const handleRowClick = (params: { row: any, $rowIndex, column, $columnIndex, cell, $event }) => {
currentRow.value = params.row
emits('current-change', params.row, params.$rowIndex, params.column, params.$columnIndex, params.cell, params.$event)
}
const handleCheckboxChange = ({ checked, records }: { checked: boolean, records: any[] }) => {
// console.log('选中的数据:', records);
// console.log('是否全选:', checked);
emits('selection-change', records)
};
// 处理行选择
const toggleRowSelection = (row: any, checked: boolean) => {
if (tableRef.value) {
tableRef.value.setCheckboxRow(row, checked)
}
}
// 获取所有选中行数据
const allSelection = () => {
return tableRef.value.getCheckboxRecords()
}
// 设置当前行
const setCurrentRow = (row: any) => {
if (tableRef.value) {
tableRef.value.setCurrentRow(row)
nextTick(() => {
tableRef.value.scrollToRow(row)
})
}
}
// 清除高亮行
const clearCurrentRow = () => {
if (tableRef.value) {
tableRef.value.clearCurrentRow()
}
}
// 初始化列拖拽功能
onMounted(() => {
if (props.enableColumnDrag && tableRef.value) {
initColumnDrag();
}
if (props.enableRowDrag && tableRef.value) {
initRowDrag();
}
});
// 监听数据变化时重置当前行
watch(() => props.tableData, (newVal) => {
// 自定义索引
if (newVal) {
props.tableData.forEach((item: any, index) => {
item._index = index + 1
})
}
currentRow.value = null
})
// 初始化列拖拽
const initColumnDrag = () => {
// 获取表头单元格
const tableHeader = tableRef.value.$el.querySelector('.vxe-table--header thead tr');
if (!tableHeader) return;
// 初始化拖拽
const sortable = new Sortable(tableHeader, {
animation: 150, // 动画时间
handle: '.el-table-header-cell', // 拖拽手柄
ghostClass: 'sortable-ghost', // 拖拽时的占位符样式
onEnd: (evt: any) => {
// console.log('evt==>', evt);
// 过滤掉固定列
const draggableColumns = props.columns.filter(col => !col.fixed);
// 如果发生了位置变化
if (evt.oldIndex !== evt.newIndex && draggableColumns.length) {
// 复制原数组并调整顺序
const newColumns = [...draggableColumns];
const [movedItem] = newColumns.splice(evt.oldIndex, 1);
newColumns.splice(evt.newIndex, 0, movedItem);
// 更新原始columns数组中对应列的位置
const updatedColumns = [...props.columns];
// 先移除所有可拖拽列
const nonDraggableColumns = updatedColumns.filter(col => col.fixed);
// 合并固定列和新顺序的可拖拽列
const resultColumns = nonDraggableColumns.concat(newColumns);
// 触发事件,通知父组件列顺序已改变
emits('column-drag-end', {
oldIndex: evt.oldIndex,
newIndex: evt.newIndex,
columns: [...resultColumns]
});
}
}
});
};
// 初始化行拖拽
const initRowDrag = () => {
const tableBody = tableRef.value.$el.querySelector('.vxe-table--body-wrapper tbody');
const sortable = new Sortable(tableBody, {
animation: 150, // 拖拽动画时长
ghostClass: 'sortable-ghost', // 拖拽占位符样式
chosenClass: 'sortable-chosen', // 选中行样式
// 拖拽结束回调
onEnd: (evt: any) => {
const { oldIndex, newIndex } = evt
if (oldIndex === newIndex) return // 未改变位置则不处理
// 调整数组顺序
const moveRow = props.tableData.splice(oldIndex, 1)[0]
props.tableData.splice(newIndex, 0, moveRow)
// 重新更新排序值
props.tableData.forEach((item: any, index) => {
item.sort = index + 1
})
// console.log("🚀 ~ initRowDrag ~ props.tableData:", props.tableData)
emits('row-drag-end', {
oldIndex,
newIndex,
list: props.tableData
})
}
})
};
// 暴露公共方法
defineExpose({
setCurrentRow,
clearCurrentRow,
toggleRowSelection,
allSelection,
tableRef,
})
</script>
<style scoped lang="scss">
.common-table-container {
height: 100%;
}
:deep(.vxe-header--row th) {
border-color: #1F6DD3;
background-color: #1F6DD3;
color: #fff;
}
:deep(.vxe-body--row td) {
border-color: #1F6DD3;
}
:deep(.vxe-table--row-hover) {
background-color: #1F6DD3;
}
:deep(.vxe-table--current-row) {
background-color: #1F6DD3;
}
:deep(.el-table-header-cell) {
cursor: move;
user-select: none;
}
// 表格边框
:deep(.vxe-table--render-default.border--full .vxe-body--column) {
background-image: linear-gradient(#96B8D9, #96B8D9), linear-gradient(#96B8D9, #96B8D9) !important;
}
// 表头边框
:deep(.vxe-table--render-default.border--full .vxe-header--column) {
background-image: linear-gradient(#1F6DD3, #1F6DD3), linear-gradient(#1F6DD3, #1F6DD3) !important;
}
</style>

16
src/env.d.ts vendored
View File

@ -1,16 +0,0 @@
import type { HTMLAttributes } from 'vue'
declare global {
namespace JSX {
interface IntrinsicElements {
[elemName: string]: HTMLAttributes
}
}
}
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}

View File

@ -1 +0,0 @@
export * from './lisData/dictData';

View File

@ -1,65 +0,0 @@
//仪器,字典数据自动格式化类
import { DictData } from "@/types/index";
import { getGroupInstrdList } from "@/api/liswork/work/LisWork";
import { comDict } from "@/utils/dict";
const dictData = ref<DictData>({});
const instrOptions = ref<{ yq: string; yqmc: string }[]>([]);
export const getYqData = (data?: any) => {
// 避免重复请求
// if (instrOptions.value.length > 0) return;
getGroupInstrdList(data).then((res: any) => {
if (res.code == 0) {
instrOptions.value = res.data.map((item: any) => ({
yq: item.yq.trim(),
yqmc: item.yqmc,
}));
}
});
};
// 正在请求中的字典标记,防止并发重复调用
const pendingDict = new Set<string>()
export const getDictData = async (...args: string[]) => {
// 过滤:无缓存 且 当前没有正在请求
const needReqKeys = args.filter(key => !(key in dictData.value) && !pendingDict.has(key))
if (needReqKeys.length === 0) return
// 标记开始请求
needReqKeys.forEach(k => pendingDict.add(k))
try {
const dictRefs = await comDict(...needReqKeys)
needReqKeys.forEach((key) => {
if (dictRefs[key] !== undefined) {
dictData.value[key] = toRaw(dictRefs[key].value) || []
}
})
} finally {
// 请求结束无论成功失败都清除标记
needReqKeys.forEach(k => pendingDict.delete(k))
}
}
//字典数据格式化方法
export const formatDict = (v: string, dictType: string) => {
if (v == undefined) return;
return (
dictData.value[dictType]?.find((item: any) => item.value == v.trim())
?.label || v
);
};
// 通用接口获取的字典数据赋值方法
export const getOptionSelect = (dictType: string, list: any[]) => {
dictData.value[dictType] = list || [];
};
//仪器格式化方法
export const formatInstr = (v: string) => {
return instrOptions.value.find((item: any) => item.yq == v.trim())?.yqmc || v;
};
export { dictData, instrOptions };

View File

@ -1,53 +0,0 @@
// 兼容verbatimModuleSyntax:拆分/标记类型与值导入
import { ElMessageBox } from "element-plus";
import type { ElMessageBoxOptions } from "element-plus";
/**
* 极简删除确认框封装(支持返回值判断)
* @param {string} [message] - 自定义删除提示信息(可选)
* @param {Partial<MessageBoxConfirmOptions>} [customOptions] - 自定义配置(可选)
* @returns {Promise<boolean>} 确认返回true,取消/关闭返回false
*/
export const deleteConfirm = (
message?: string,
customOptions?: Partial<ElMessageBoxOptions>
) => {
// 删除场景默认配置
const defaultOptions: ElMessageBoxOptions = {
title: "删除确认",
message: message || "此操作将永久删除该数据,无法恢复,是否继续?",
confirmButtonText: "确认删除",
cancelButtonText: "取消",
type: "error",
//center: true,
};
// 合并默认配置与自定义配置
const finalOptions = { ...defaultOptions, ...customOptions };
// 核心修改:无论确认/取消,都resolve布尔值,不触发reject
return new Promise<boolean>((resolve) => {
ElMessageBox.confirm(finalOptions.message, finalOptions.title, finalOptions)
.then(() => resolve(true)) // 确认:返回true
.catch(() => resolve(false)); // 取消/关闭:返回false(不再reject)
});
};
/**
* 使用示例
const handleDelete = async (id: number) => {
// 直接获取返回值:true=确认,false=取消
const isConfirmed = await deleteConfirm('是否删除数据?', { title: '自定义标题', type: 'warning' })
if (isConfirmed) {
// 确认删除:执行业务逻辑
// await api.deleteData(id) // 真实接口请求
ElMessage.success(`ID为${id}的数据删除成功!`)
console.log(`执行删除操作,数据ID:${id}`)
} else {
// 取消删除:给出提示(可选)
ElMessage.info('已取消删除操作')
console.log('用户取消删除')
}
}
*/

View File

@ -3,7 +3,7 @@
<router-view v-slot="{ Component, route }">
<transition name="fade-transform" mode="out-in">
<keep-alive :include="tagsViewStore.cachedViews">
<component v-if="!route.meta.link" :is="Component" :key="route.path" />
<component v-if="!route.meta.link" :is="Component" :key="route.path"/>
</keep-alive>
</transition>
</router-view>
@ -21,24 +21,24 @@ const tagsViewStore = useTagsViewStore()
<style lang="scss" scoped>
.app-main {
/* 50= navbar 50 */
min-height: calc(100vh - 3.125rem);
min-height: calc(100vh - 50px);
width: 100%;
position: relative;
overflow: hidden;
}
.fixed-header+.app-main {
padding-top: 3.125rem;
.fixed-header + .app-main {
padding-top: 50px;
}
.hasTagsView {
.app-main {
/* 84 = navbar + tags-view = 50 + 34 */
min-height: calc(100vh - 5.25rem);
min-height: calc(100vh - 84px);
}
.fixed-header+.app-main {
padding-top: 5.25rem;
.fixed-header + .app-main {
padding-top: 84px;
}
}
</style>
@ -65,3 +65,4 @@ const tagsViewStore = useTagsViewStore()
border-radius: 3px;
}
</style>

View File

@ -1,34 +1,27 @@
<template>
<div class="navbar">
<!-- 侧边栏折叠按钮(保留原有功能) -->
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container"
@toggleClick="toggleSideBar" />
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!settingsStore.topNav" />
<top-nav id="topmenu-container" class="topmenu-container" v-if="settingsStore.topNav" />
<!-- 核心改造:替换面包屑/顶部导航,改为产品名称 + 登录信息布局 -->
<div class="custom-header-content">
<!-- 左侧:APP产品名称 -->
<div class="app-name">
{{ formatDict(userStore.sysLoginParam.loginYLJG || '1', 'HOS') }}
</div>
<!-- 右侧:功能按钮 + 登录信息 + 原生设置菜单 -->
<div class="right-content">
<!-- 保留原有功能按钮(搜索、全屏、布局大小)
<div class="right-menu">
<template v-if="appStore.device !== 'mobile'">
<header-search id="header-search" class="right-menu-item" />
<!-- <el-tooltip content="源码地址" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip> -->
<!-- <el-tooltip content="文档地址" effect="dark" placement="bottom">
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
</el-tooltip> -->
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="布局大小" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
</template>
-->
<!-- 登录人信息(姓名 + 角色) -->
<div class="user-info" v-if="userStore.nickName">
欢迎,{{ userStore.nickName }}({{ userStore.roleName }})
</div>
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<!-- 原生个人信息/设置菜单(保留原有功能) -->
<div class="avatar-container">
<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
<div class="avatar-wrapper">
@ -43,9 +36,6 @@
<el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
<span>布局设置</span>
</el-dropdown-item>
<el-dropdown-item command="clearCache">
<span>清除缓存</span>
</el-dropdown-item>
<el-dropdown-item divided command="logout">
<span>退出登录</span>
</el-dropdown-item>
@ -55,12 +45,10 @@
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ElMessageBox, ElMessage } from 'element-plus'
// 引入原有组件
import { ElMessageBox } from 'element-plus'
import Breadcrumb from '@/components/Breadcrumb'
import TopNav from '@/components/TopNav'
import Hamburger from '@/components/Hamburger'
@ -69,35 +57,14 @@ import SizeSelect from '@/components/SizeSelect'
import HeaderSearch from '@/components/HeaderSearch'
import RuoYiGit from '@/components/RuoYi/Git'
import RuoYiDoc from '@/components/RuoYi/Doc'
// 引入Store
import useAppStore from '@/store/modules/app'
import useUserStore from '@/store/modules/user'
import useSettingsStore from '@/store/modules/settings'
// 引入图标和默认头像(可替换为你的默认头像)
import { CaretBottom } from '@element-plus/icons-vue'
import { formatDict, getDictData } from '@/hooks'
const appStore = useAppStore()
const userStore = useUserStore()
const settingsStore = useSettingsStore()
import { useRoute } from 'vue-router';
const route = useRoute();
// 处理角色名称(取第一个角色,多角色可自定义)
const roleName = computed(() => {
if (!userStore.roles || userStore.roles.length === 0) {
return '普通用户'
}
// 角色编码映射表:key 是你的 roles 编码,value 是要显示的中文
const roleMap = {
'ROLE_DEFAULT': '普通用户',
'admin': '管理员',
'ROLE_ADMIN': '超级管理员',
'ROLE_OPERATOR': '操作员' // 按你的实际角色编码添加
}
// 取第一个角色编码,映射为中文
return roleMap[userStore.roles[0]] || userStore.roles[0]
})
// 原有方法保留
function toggleSideBar() {
appStore.toggleSideBar()
}
@ -107,9 +74,6 @@ function handleCommand(command) {
case "setLayout":
setLayout();
break;
case "clearCache":
clearCache();
break;
case "logout":
logout();
break;
@ -118,31 +82,6 @@ function handleCommand(command) {
}
}
const clearCache = () => {
console.log(route.path);
const routeColumnKeyMap = {
'/liswork/work': 'cgjyList_column_config', // 检验列表页面
'/liswork/micro': 'wswList_column_config', // 微生物列表页面
};
// 获取当前页面的缓存KEY
const currentKey = routeColumnKeyMap[route.path];
if (!currentKey) {
ElMessage.warning('当前页面无表格列缓存可清除');
return;
}
try {
localStorage.removeItem(currentKey);
ElMessage.success('当前页面表格列缓存已清除');
// 可选:刷新页面使配置生效(或通知对应页面重置列)
location.reload();
} catch (e) {
console.error('清除当前列缓存失败', e);
ElMessage.error('清除缓存失败');
}
}
function logout() {
ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定',
@ -152,41 +91,27 @@ function logout() {
userStore.logOut().then(() => {
location.href = '/index';
})
}).catch(() => {
});
}).catch(() => { });
}
const emits = defineEmits(['setLayout'])
function setLayout() {
emits('setLayout');
}
onMounted(() => {
getDictData('HOS')
})
</script>
<style lang='scss' scoped>
.navbar {
height: 3.125rem;
/* 固定高度,与原有一致 */
height: 50px;
overflow: hidden;
position: relative;
//background: #fff;
background: #304156;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.15);
//box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
display: flex;
/* 改为flex布局,确保子元素对齐 */
align-items: center;
/* 垂直居中 */
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.hamburger-container {
line-height: 3.125rem;
/* 与导航栏高度一致 */
line-height: 46px;
height: 100%;
padding: 0 1rem;
float: left;
cursor: pointer;
transition: background 0.3s;
-webkit-tap-highlight-color: transparent;
@ -196,59 +121,35 @@ onMounted(() => {
}
}
/* 核心布局:产品名称 + 右侧内容容器 */
.custom-header-content {
display: flex;
align-items: center;
justify-content: space-between;
flex: 1;
/* 占满剩余宽度 */
padding: 0 1.25rem;
.breadcrumb-container {
float: left;
}
.topmenu-container {
position: absolute;
left: 50px;
}
.errLog-container {
display: inline-block;
vertical-align: top;
}
.right-menu {
float: right;
height: 100%;
margin-left: .3125rem;
}
/* 产品名称样式 */
.app-name {
font-size: 1.125rem;
font-weight: 600;
color: #fff;
/* 若依主题色,可替换为品牌色 */
white-space: nowrap;
/* 防止换行 */
font-style: italic;
/* 关键:字体倾斜 */
/* 可选优化:增加倾斜角度控制(默认 italic 约 14 度,可自定义) */
/* transform: skewX(-10deg); /* 向左倾斜 10 度,正负值控制倾斜方向 */
}
/* 右侧内容容器(功能按钮 + 用户信息 + 头像菜单) */
.right-content {
line-height: 50px;
display: flex;
align-items: center;
gap: .75rem;
/* 元素间距,可调整 */
white-space: nowrap;
/* 防止换行 */
&:focus {
outline: none;
}
/* 用户信息样式 */
.user-info {
font-size: .875rem;
font-weight: 600;
display: flex;
align-items: center;
color: #e6f7f4;
}
/* 原有右侧菜单样式调整 */
.right-menu-item {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0 .5rem;
display: inline-block;
padding: 0 8px;
height: 100%;
font-size: 1.125rem;
font-size: 18px;
color: #5a5e66;
vertical-align: text-bottom;
@ -262,38 +163,29 @@ onMounted(() => {
}
}
/* 头像容器样式调整 */
.avatar-container {
margin-right: 40px;
.avatar-wrapper {
margin-top: 5px;
position: relative;
display: flex;
align-items: center;
gap: .25rem;
/* 头像与下拉图标间距 */
.user-avatar {
cursor: pointer;
width: 2.25rem;
/* 调整头像大小 */
height: 2.25rem;
border-radius: 50%;
/* 圆形头像,可改为10px保持原有风格 */
object-fit: cover;
/* 防止头像变形 */
width: 40px;
height: 40px;
border-radius: 10px;
}
i {
cursor: pointer;
font-size: .75rem;
color: #5a5e66;
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
}
}
}
/* 隐藏原有面包屑和顶部导航(已替换) */
.breadcrumb-container,
.topmenu-container {
display: none !important;
}
}
</style>

View File

@ -1,191 +0,0 @@
<template>
<div class="navbar">
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!settingsStore.topNav" />
<top-nav id="topmenu-container" class="topmenu-container" v-if="settingsStore.topNav" />
<div class="right-menu">
<template v-if="appStore.device !== 'mobile'">
<header-search id="header-search" class="right-menu-item" />
<!-- <el-tooltip content="源码地址" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip> -->
<!-- <el-tooltip content="文档地址" effect="dark" placement="bottom">
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
</el-tooltip> -->
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="布局大小" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
</template>
<div class="avatar-container">
<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
<div class="avatar-wrapper">
<img :src="userStore.avatar" class="user-avatar" />
<el-icon><caret-bottom /></el-icon>
</div>
<template #dropdown>
<el-dropdown-menu>
<router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item>
</router-link>
<el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
<span>布局设置</span>
</el-dropdown-item>
<el-dropdown-item divided command="logout">
<span>退出登录</span>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</div>
</template>
<script setup>
import { ElMessageBox } from 'element-plus'
import Breadcrumb from '@/components/Breadcrumb'
import TopNav from '@/components/TopNav'
import Hamburger from '@/components/Hamburger'
import Screenfull from '@/components/Screenfull'
import SizeSelect from '@/components/SizeSelect'
import HeaderSearch from '@/components/HeaderSearch'
import RuoYiGit from '@/components/RuoYi/Git'
import RuoYiDoc from '@/components/RuoYi/Doc'
import useAppStore from '@/store/modules/app'
import useUserStore from '@/store/modules/user'
import useSettingsStore from '@/store/modules/settings'
const appStore = useAppStore()
const userStore = useUserStore()
const settingsStore = useSettingsStore()
function toggleSideBar() {
appStore.toggleSideBar()
}
function handleCommand(command) {
switch (command) {
case "setLayout":
setLayout();
break;
case "logout":
logout();
break;
default:
break;
}
}
function logout() {
ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
userStore.logOut().then(() => {
location.href = '/index';
})
}).catch(() => { });
}
const emits = defineEmits(['setLayout'])
function setLayout() {
emits('setLayout');
}
</script>
<style lang='scss' scoped>
.navbar {
height: 50px;
overflow: hidden;
position: relative;
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.hamburger-container {
line-height: 46px;
height: 100%;
float: left;
cursor: pointer;
transition: background 0.3s;
-webkit-tap-highlight-color: transparent;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
}
.breadcrumb-container {
float: left;
}
.topmenu-container {
position: absolute;
left: 50px;
}
.errLog-container {
display: inline-block;
vertical-align: top;
}
.right-menu {
float: right;
height: 100%;
line-height: 50px;
display: flex;
&:focus {
outline: none;
}
.right-menu-item {
display: inline-block;
padding: 0 8px;
height: 100%;
font-size: 18px;
color: #5a5e66;
vertical-align: text-bottom;
&.hover-effect {
cursor: pointer;
transition: background 0.3s;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
}
}
.avatar-container {
margin-right: 40px;
.avatar-wrapper {
margin-top: 5px;
position: relative;
.user-avatar {
cursor: pointer;
width: 40px;
height: 40px;
border-radius: 10px;
}
i {
cursor: pointer;
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
}
}
}
}
}
</style>

View File

@ -8,10 +8,8 @@
<img src="@/assets/images/dark.svg" alt="dark" />
<div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
<i aria-label="图标: check" class="anticon anticon-check">
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true"
focusable="false" class>
<path
d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" />
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
<path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" />
</svg>
</i>
</div>
@ -20,10 +18,8 @@
<img src="@/assets/images/light.svg" alt="light" />
<div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
<i aria-label="图标: check" class="anticon anticon-check">
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true"
focusable="false" class>
<path
d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" />
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
<path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" />
</svg>
</i>
</div>
@ -32,7 +28,7 @@
<div class="drawer-item">
<span>主题颜色</span>
<span class="comp-style">
<el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange" />
<el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange"/>
</span>
</div>
<el-divider />
@ -96,7 +92,7 @@ const showSettings = ref(false);
const theme = ref(settingsStore.theme);
const sideTheme = ref(settingsStore.sideTheme);
const storeSettings = computed(() => settingsStore);
const predefineColors = ref(["#1F6DD3", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]);
const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]);
/** 是否需要topnav */
function topNavChange(val) {
@ -148,12 +144,10 @@ defineExpose({
color: rgba(0, 0, 0, 0.85);
line-height: 22px;
font-weight: bold;
.drawer-title {
font-size: 14px;
}
}
.setting-drawer-block-checbox {
display: flex;
justify-content: flex-start;

View File

@ -1,10 +1,17 @@
<template>
<div id="tags-view-container" class="tags-view-container">
<scroll-pane ref="scrollPaneRef" class="tags-view-wrapper" @scroll="handleScroll">
<router-link v-for="tag in visitedViews" :key="tag.path" :data-path="tag.path"
:class="isActive(tag) ? 'active' : ''" :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
class="tags-view-item" :style="activeStyle(tag)" @click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
@contextmenu.prevent="openMenu(tag, $event)">
<router-link
v-for="tag in visitedViews"
:key="tag.path"
:data-path="tag.path"
:class="isActive(tag) ? 'active' : ''"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
class="tags-view-item"
:style="activeStyle(tag)"
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
@contextmenu.prevent="openMenu(tag, $event)"
>
{{ tag.title }}
<span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)">
<close class="el-icon-close" style="width: 1em; height: 1em;vertical-align: middle;" />
@ -236,122 +243,66 @@ function handleScroll() {
<style lang='scss' scoped>
.tags-view-container {
height: 2.125rem;
/* 稍微增加高度,让立体效果更明显 */
height: 34px;
width: 100%;
background: #f5f7fa;
/* 标签栏整体背景(浅灰,与标签区分) */
/* 1. 去掉底部边框(关键:消除物理边框导致的缝隙) */
border-bottom: none;
/* 2. 弱化或去掉标签栏的底部阴影(避免阴影造成视觉缝隙) */
box-shadow: 0 .0625rem .125rem rgba(0, 0, 0, 0.02);
/* 仅保留极浅的顶部阴影 */
/* 标签栏底部加一条浅线,区分标签栏和下方页面 */
border-bottom: .0625rem solid #e0e0e0;
background: #fff;
border-bottom: 1px solid #d8dce5;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
.tags-view-wrapper {
.tags-view-item {
/* 未激活标签:灰色渐变+立体阴影 */
display: inline-block;
position: relative;
cursor: pointer;
height: 1.75rem;
line-height: 1.75rem;
padding: 0 .75rem;
font-size: .75rem;
margin-left: .25rem;
margin-top: .25rem;
border-radius: .25rem .25rem 0 0;
/* 底部圆角去掉,与下方无缝感 */
border: 1px solid #d0d0d0;
/* 浅灰边框 */
border-bottom-color: transparent;
/* 底部边框透明,避免与标签栏线重叠 */
/* 灰色渐变:从上到下浅灰→深灰(模拟轻微凸起) */
background: linear-gradient(180deg, #f0f0f0 0%, #e0e0e0 100%);
/* 立体阴影:底部轻微阴影+顶部内高光 */
box-shadow:
0 2px 4px rgba(0, 0, 0, 0.1),
/* 底部阴影加深,突出悬浮感 */
inset 0 1px 1px rgba(255, 255, 255, 0.8);
/* 顶部内高光,增强凸起感 */
color: #777;
/* 文字灰色,不抢眼 */
transition: all 0.2s ease;
/* 过渡动画 */
/* 鼠标悬停时增强效果(未激活状态) */
&:hover:not(.active) {
background: linear-gradient(180deg, #f5f5f5 0%, #e8e8e8 100%);
box-shadow:
0 3px 4px rgba(0, 0, 0, 0.08),
inset 0 1px 0 rgba(255, 255, 255, 0.9);
border-color: #d0d0d0;
color: #333;
/* hover时文字略深 */
height: 26px;
line-height: 26px;
border: 1px solid #d8dce5;
color: #495060;
background: #fff;
padding: 0 8px;
font-size: 12px;
margin-left: 5px;
margin-top: 4px;
&:first-of-type {
margin-left: 15px;
}
&:last-of-type {
margin-right: 15px;
}
/* 激活标签样式:深色渐变+强阴影,突出当前页 */
&.active {
/* 激活标签:从左到右的主题色渐变(示例用蓝紫色) */
background: linear-gradient(90deg, #409eff 0%, #6b7aff 100%);
color: #ffffff;
/* 文字白色,与渐变背景对比 */
border-color: #409eff;
/* 边框与渐变颜色匹配 */
box-shadow: 0 3px 6px rgba(64, 158, 255, 0.25),
/* 外阴影(带颜色倾向,增强立体感) */
inset 0 1px 0 rgba(255, 255, 255, 0.2);
/* 内阴影(顶部亮边,模拟高光) */
transform: translateY(-1px);
/* 轻微上浮,与其他标签区分 */
background-color: #42b983;
color: #fff;
border-color: #42b983;
&::before {
content: "";
background: #fff;
display: inline-block;
width: .5rem;
height: .5rem;
width: 8px;
height: 8px;
border-radius: 50%;
position: relative;
margin-right: .3125rem;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
/* 小圆圈阴影,增强立体 */
margin-right: 5px;
}
}
}
}
/* 右键菜单也增加立体效果 */
.contextmenu {
margin: 0;
background: linear-gradient(180deg, #ffffff 0%, #f9fafb 100%);
background: #fff;
z-index: 3000;
position: absolute;
list-style-type: none;
padding: 5px 0;
border-radius: 6px;
border-radius: 4px;
font-size: 12px;
font-weight: 400;
color: #333;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15),
/* 外阴影 */
inset 0 1px 0 rgba(255, 255, 255, 0.8);
/* 内阴影 */
border: 1px solid #e5e7eb;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
li {
margin: 0;
padding: 7px 16px;
cursor: pointer;
&:hover {
background: #f0f5ff;
/* hover 时浅蓝色背景,与整体风格呼应 */
background: #eee;
}
}
}
@ -363,25 +314,23 @@ function handleScroll() {
.tags-view-wrapper {
.tags-view-item {
.el-icon-close {
width: 1rem;
height: 1rem;
vertical-align: .125rem;
width: 16px;
height: 16px;
vertical-align: 2px;
border-radius: 50%;
text-align: center;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
transform-origin: 100% 50%;
&:before {
transform: scale(0.6);
display: inline-block;
vertical-align: -0.1875rem;
vertical-align: -3px;
}
&:hover {
background-color: #b4bccc;
color: #fff;
width: .75rem !important;
height: .75rem !important;
width: 12px !important;
height: 12px !important;
}
}
}

View File

@ -1,6 +1,6 @@
<template>
<div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
<sidebar v-if="!sidebar.hide" class="sidebar-container" />
<div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
<div :class="{ 'fixed-header': fixedHeader }">
@ -21,10 +21,7 @@ import defaultSettings from '@/settings'
import useAppStore from '@/store/modules/app'
import useSettingsStore from '@/store/modules/settings'
import { initWebSocket } from '@/utils/webSocket'
import { useCommonStore } from '@/store/modules/commonStore'
const mbStore = useCommonStore();
const settingsStore = useSettingsStore()
const theme = computed(() => settingsStore.theme);
const sideTheme = computed(() => settingsStore.sideTheme);
@ -63,17 +60,11 @@ const settingRef = ref(null);
function setLayout() {
settingRef.value.openSetting();
}
onMounted(() => {
// 初始化socket
const url = `ws://47.97.125.165:8904/ws/msgserver/${mbStore.fingerprint}`
initWebSocket({ fullUrl: url })
})
</script>
<style lang="scss" scoped>
@import "@/assets/styles/mixin.scss";
@import "@/assets/styles/variables.module.scss";
@import "@/assets/styles/mixin.scss";
@import "@/assets/styles/variables.module.scss";
.app-wrapper {
@include clearfix;

View File

@ -1,28 +1,17 @@
import {
createApp
} from 'vue'
import { createApp } from 'vue'
import Cookies from 'js-cookie'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import locale from 'element-plus/es/locale/lang/zh-cn'
import VxeUI from 'vxe-pc-ui'
import 'vxe-pc-ui/lib/style.css'
import VXETable from 'vxe-table'
import 'vxe-table/lib/style.css'
import '@/assets/styles/index.scss' // global css
import '@/utils/polyfills';
import {
setupRem
} from './utils/rem';
setupRem(); // 初始化rem适配
import '@/assets/styles/index.scss' // global css
// 先导入 jQuery 并设置全局变量
// import * as $ from 'jquery';
// window.jQuery = $;
// window.$ = $;
import * as $ from 'jquery';
window.jQuery = $;
window.$ = $;
import App from './App'
import store from './store'
@ -31,9 +20,7 @@ import directive from './directive' // directive
// 注册指令
import plugins from './plugins' // plugins
import {
download
} from '@/utils/request'
import { download } from '@/utils/request'
// svg图标
import 'virtual:svg-icons-register'
@ -42,18 +29,8 @@ import elementIcons from '@/components/SvgIcon/svgicon'
import './permission' // permission control
import {
useDict,
comDict
} from '@/utils/dict'
import {
parseTime,
resetForm,
addDateRange,
handleTree,
selectDictLabel,
selectDictLabels
} from '@/utils/ruoyi'
import {useDict, comDict} from '@/utils/dict'
import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi'
// 分页组件
import Pagination from '@/components/Pagination'
@ -71,14 +48,7 @@ import ImagePreview from "@/components/ImagePreview"
import TreeSelect from '@/components/TreeSelect'
// 字典标签组件
import DictTag from '@/components/DictTag'
// elTable表格组件
import CustomTable from '@/components/elTable'
// vxeTable 组件
import CustomVxeTable from '@/components/vxeTable'
// 下拉框表格组件
import SelectTable from '@/components/SelectTable'
import print from 'vue3-print-nb';
import Vue3SeamlessScroll from 'vue3-seamless-scroll' // 引入插件
import Print from 'vue3-print-nb';
const app = createApp(App)
// 全局方法挂载
@ -101,14 +71,10 @@ app.component('ImageUpload', ImageUpload)
app.component('ImagePreview', ImagePreview)
app.component('RightToolbar', RightToolbar)
app.component('Editor', Editor)
app.component('CustomTable', CustomTable)
app.component('CustomVxeTable', CustomVxeTable)
app.component('SelectTable', SelectTable)
//VUE3标准方法注入全局方法
app.provide('comDict', comDict);
app.use(print); // 注册打印插件
app.use(Vue3SeamlessScroll)
app.use(Print); // 注册打印插件
app.provide('$vuePrint', app.config.globalProperties.$vuePrint);
app.use(router)
app.use(store)
@ -116,8 +82,7 @@ app.use(plugins)
app.use(elementIcons)
app.component('svg-icon', SvgIcon)
directive(app)
app.use(VxeUI)
app.use(VXETable)
// 使用element-plus 并且设置全局的大小
app.use(ElementPlus, {
locale: locale,

Some files were not shown because too many files have changed in this diff Show More