Compare commits

..

73 Commits

Author SHA1 Message Date
shih
c5ff4c263f 获取字典表格选择器值回显文本方法 key 修改成与后台一致 2026-03-25 11:54:56 +08:00
mll
eeb6f27d47 综合销售表合计回显 搜索报错时给通过 2026-03-24 13:25:13 +08:00
mll
b2a130930c 综合销售表合计 2026-03-23 21:17:34 +08:00
mll
b35b04452a 一级列小于14个列宽自适应,字段width只有列大于等于14个才生效,合计行的topborder,隐藏selection 2026-03-23 00:12:59 +08:00
mll
081544019c 黑色边框 2026-03-19 23:03:02 +08:00
mll
e545aa36ca 报表行颜色修改 客户名称等搜索条件组件改远程搜索 2026-03-19 22:47:02 +08:00
mll
85f7484c57 search项默认显示4条 2026-03-18 18:41:47 +08:00
mll
cb77ad837b 综合销售表维度默认年 月 2026-03-18 16:21:45 +08:00
mll
03c8d49a9c 修复 2026-03-18 11:24:21 +08:00
mll
201a4464bb 维度开启时data没有返回的字段隐藏 2026-03-18 11:14:27 +08:00
mll
e8fce71d0e 达成率字段始终显示 2026-03-18 10:45:56 +08:00
mll
774895f901 维度隐藏列多选问题 去掉隐藏搜索等 2026-03-18 10:35:03 +08:00
mll
386fb026b5 isHideCol隐藏列调试修改 去掉子字段和字段分组 根据字典cssClass的值设置整行颜色 2026-03-17 15:43:52 +08:00
mll
280ff0837f 去掉搜索区 查询:,搜索按钮改文字查询 2026-03-17 11:51:45 +08:00
mll
6625e0c1a3 报表页面高度固定 合计列index和selection开启时并两行,剂型名称 销售类型 业务区域下拉数据接口对接 2026-03-17 11:34:00 +08:00
mll
354f839b4e 去掉选列数量提示 调整search行高 查询移到内部 动态表头优化 报表条数改为,默认50 2026-03-16 23:35:21 +08:00
mll
5b95cc85f0 效期剩余时间(天)分类搜索下拉框数据获取 label宽度和searchWidth调整 2026-03-16 13:20:12 +08:00
mll
cf511deeef 多选(in) multiple和月份表头按data是否返回字段过滤 2026-03-14 23:46:24 +08:00
mll
2217b14377 设置当年dataType为string以免不回显,配置报表页面edirow时底部滚动条不变 2026-03-14 12:07:08 +08:00
mll
d57c46b531 模糊查询都是多选 2026-03-13 15:21:44 +08:00
mll
b6d970175e 货品下拉框等改多选 2026-03-13 15:11:22 +08:00
mll
b9c60b8c48 Merge branch 'main' of http://8.130.49.250:3000/admin/gr_report_web 2026-03-12 16:23:02 +08:00
mll
fbe8c8ba4e 业务员搜索下拉 2026-03-12 16:22:59 +08:00
chy
4260e35018 121 2026-03-12 14:01:28 +08:00
chy
89b3c978c5 Merge branch 'main' of http://8.130.49.250:3000/admin/gr_report_web
# Conflicts:
#	src/views/lowdesign/reportDesign/index.vue
2026-03-12 13:58:48 +08:00
chy
ccfb06113a 1231 2026-03-12 13:57:37 +08:00
mll
e33fcb33fe Merge branch 'main' of http://8.130.49.250:3000/admin/gr_report_web 2026-03-12 12:37:31 +08:00
mll
5154baf6a7 剂型名称 客户名称 货品名称改为下拉框 2026-03-12 12:37:20 +08:00
chy
957ebbe28d 12 2026-03-11 00:13:41 +08:00
mll
ca4ebdfb7c 透明登录框 2026-03-10 22:04:36 +08:00
mll
b34b599513 大屏跳转_blank 2026-03-10 13:19:59 +08:00
mll
31df51dcc6 Merge branch 'main' of http://8.130.49.250:3000/admin/gr_report_web
# Conflicts:
#	src/views/Login/components/LoginForm.vue
2026-03-07 00:25:34 +08:00
mll
0caec67d4a 单点修改 2026-03-07 00:24:15 +08:00
mll
5b3c7d8327 搜索时年月的下拉选择显示 2026-03-05 15:35:36 +08:00
chy
2c848827b3 Merge branch 'main' of http://8.130.49.250:3000/admin/gr_report_web 2026-03-03 15:04:46 +08:00
chy
8ec002300b 统一身份认证修改 2026-03-03 15:04:34 +08:00
mll
2364ff6278 登录页图标缩小 2026-03-02 17:22:21 +08:00
DESKTOP-AD8UBUJ\ling
901572cd89 test 2026-03-02 16:33:38 +08:00
DESKTOP-AD8UBUJ\ling
19ecd53269 Revert "年月搜索框调小 登录页图标调小"
This reverts commit 54741347130a83ad189d058d996bf0aa0e9f4540.
2026-03-02 16:33:38 +08:00
DESKTOP-AD8UBUJ\ling
abfd398b86 年月搜索框调小 登录页图标调小 2026-03-02 16:33:38 +08:00
chy
824dd39172 修改统一身份认证 2026-03-02 15:13:17 +08:00
chy
76df67db6a 修改启动说明 2026-03-02 11:55:46 +08:00
chy
ae12c765be 修改统一身份认证 2026-03-02 10:35:21 +08:00
chy
40b95aae29 Merge branch 'main' of http://8.130.49.250:3000/admin/gr_report_web 2026-02-28 13:43:27 +08:00
chy
81813cbdb6 修改登陆界面 2026-02-28 13:42:53 +08:00
mll
863def2d7a Merge branch 'main' of http://8.130.49.250:3000/admin/gr_report_web 2026-02-27 22:48:55 +08:00
mll
1dcfeed999 隐藏搜索条件 2026-02-27 22:48:51 +08:00
chy
ed058096be 修改登录 2026-02-27 14:00:45 +08:00
mll
47168c77d8 登录页背景自适应 登录框居中 2026-02-27 13:32:15 +08:00
mll
a8db600e90 同上 2026-02-26 12:06:23 +08:00
mll
baf731526f 维度和查询分割开 2026-02-26 12:05:34 +08:00
mll
c8d7fdd0f6 查询默认值 2026-02-25 15:46:17 +08:00
mll
c47dba53d9 Merge branch 'main' of http://8.130.49.250:3000/admin/gr_report_web 2026-02-23 14:48:08 +08:00
mll
e44d018f20 登录页1 2026-02-23 14:48:02 +08:00
chy
51922734ba 修改登录标题 2026-02-22 21:52:57 +08:00
mll
1af5fbd87c Merge branch 'main' of http://8.130.49.250:3000/admin/gr_report_web 2026-02-12 21:47:58 +08:00
mll
f2b5dd2a8d 固定多级表头 2026-02-12 21:47:53 +08:00
chy
36b4608ddf 111 2026-02-12 15:51:18 +08:00
chy
49c5d07b65 111 2026-02-12 14:26:25 +08:00
chy
dced583c7f 修改登录界面 2026-02-12 14:25:27 +08:00
chy
aa853f539b 解决冲突 2026-02-12 09:32:15 +08:00
chy
e7dd16454c Merge branch 'zh'
# Conflicts:
#	src/components/LowDesign/src/LowReport/index.vue
#	src/views/lowdesign/reportDesign/designData.ts
2026-02-12 08:50:42 +08:00
demo
b40163b2e7 动态多维表头 2026-02-11 20:15:26 +08:00
mll
cecaaacf4b view表单斑马纹 2026-02-11 16:57:52 +08:00
mll
76987e8ea6 Merge branch 'main' of http://8.130.49.250:3000/admin/gr_report_web 2026-02-11 11:30:43 +08:00
mll
7555c7093d no message 2026-02-11 11:30:30 +08:00
5ae5353baa Merge pull request 'main_beta' (#4) from main_beta into main
Reviewed-on: #4
2026-02-11 10:04:32 +08:00
mll
09a300e98a Merge branch 'main' of http://8.130.49.250:3000/admin/gr_report_web 2026-02-10 22:17:30 +08:00
mll
7a496e6407 加隐藏列 2026-02-10 22:17:26 +08:00
chy
e69cffbdb6 Merge branch 'main_beta'
# Conflicts:
#	src/views/Home/Index20.vue
2026-02-10 09:52:20 +08:00
mll
bcac3326a3 维度隐藏列 2026-02-09 20:16:36 +08:00
chy
f9393dfca8 1 2026-02-09 18:11:10 +08:00
chy
67fe2e22fc 修改国际化 2026-02-09 13:36:16 +08:00
33 changed files with 1037 additions and 276 deletions

2
.env
View File

@@ -1,5 +1,5 @@
# 标题
VITE_APP_TITLE=国瑞报表系统
VITE_APP_TITLE=国瑞药业报表系统
# 项目本地运行端口号
VITE_PORT=80

View File

@@ -4,7 +4,7 @@ VITE_NODE_ENV=production
VITE_DEV=false
# 请求路径
VITE_BASE_URL='http://127.0.0.1:48080'
VITE_BASE_URL='http://192.168.1.241'
# 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务
VITE_UPLOAD_TYPE=server

View File

@@ -89,4 +89,7 @@ export const batchGetTableList = (reportCodes: string, data?) => {
return request.post({ url: `/lideeyunji/report-data/batch/list/${reportCodes}`, data })
}
//获取综合销售报表合计
export const getAggMonthTotalDetail = (data) => {
return request.post({ url: `lideeyunji/agg/month/total/detail`, data })
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

BIN
src/assets/imgs/jsc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
src/assets/imgs/sjzt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
src/assets/imgs/szls.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
src/assets/imgs/wlzt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
src/assets/imgs/znbb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -15,7 +15,7 @@ defineProps({
</script>
<template>
<ElCard :class="[prefixCls, 'mb-15px']" shadow="never">
<ElCard :class="[prefixCls]" shadow="never">
<template v-if="title" #header>
<div class="flex items-center">
<span class="text-16px font-700">{{ title }}</span>

View File

@@ -4,6 +4,7 @@
class="low-reoprt"
:class="[`low-report__${reportCode}`, { summary: tableOption.showSummary }]"
>
<avue-crud
ref="crudRef"
v-model:search="tableSearch"
@@ -12,6 +13,7 @@
:option="tableOption"
v-bind="crudBind"
:summary-method=summaryMethod1
:row-style="rowStyleMethod"
@search-change="searchChange"
@search-reset="resetChange"
@refresh-change="refreshChange"
@@ -20,6 +22,9 @@
@selection-change="selectionChange"
@sort-change="sortChange"
>
<template #search>
<!-- <div v-if="isSearch" style="margin-bottom:6px">查询</div> -->
</template>
<!-- 自定义表格头部操作 -->
<template #menu-left="{ size }">
<ElButton
@@ -38,6 +43,31 @@
<Icon :size="14" icon="clarity:export-line"></Icon>
<span>{{ t('Avue.crud.excelBtn') }}</span>
</ElButton>
<div ref="tableHeightRef"></div>
</template>
<template v-for="prop in searchReportSlots" :key="prop" #[`${prop}-search`]="scope" >
<div>
<el-select
v-model="tableSearch[prop]"
:multiple="scope.column.multiple"
filterable
remote
remote-show-suffix
clearable
:disabled="loading"
:collapse-tags="scope.column.collapseTags"
:collapse-tags-tooltip="scope.column.collapseTagsTooltip"
:placeholder="'请选择'+scope.column.label"
:remote-method="(query) => remoteMethod(query, scope.column)"
>
<el-option
v-for="(item,index) in scope.column.dicData"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</template>
<template v-for="prop in numberRange" :key="prop" #[`${prop}-search`]="scope">
<InputNumberRange
@@ -68,11 +98,14 @@
></InputTimeRange>
</template>
<template #header v-if="Object.keys(dimensionFields)?.length">
<div style="display:flex;margin-left:40px">
<el-checkbox-group @change="searchDimension" v-model="tableSearch['Group by']"
<div style="display:flex;align-items:center;border-top:1px solid #eee;margin-bottom:10px;padding-top:10px;">
<span style="margin-right:10px;">维度:</span>
<el-checkbox-group
@change="searchDimension" v-model="tableSearch['Group by']"
placeholder="请选择内容">
<template v-for="(item,key) in dimensionFields">
<el-checkbox v-if="tableSearch['Group by']&&tableSearch['Group by'].includes(key)||!tableSearch['Group by']?.length"
<el-checkbox
v-if="1"
:key="key"
:value="key"
:label="item.label"></el-checkbox>
@@ -88,11 +121,13 @@
import * as ReportApi from '@/api/design/report'
import { InputNumberRange,InputDateRange ,InputDateTimeRange,InputTimeRange} from '../shareControl/index'
import download from '@/utils/download'
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
import { cloneDeep } from 'lodash-es'
import { useWindowSize } from '@vueuse/core'
import { assembleLengObj } from '@/utils/lowDesign'
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
import Avue from '@smallwei/avue'
import { Console } from 'console'
defineOptions({ name: 'LowReport' })
interface Props {
@@ -115,19 +150,23 @@ const { mergeLocaleMessage, t } = useI18n() // 国际化
const loading = ref(false) // 列表的加载中
const isInit = ref(false)
const isSearch = ref(false)
const tableHeightRef = ref<any>(null)
const tableOption = ref<any>({})
const tableData = ref<any>([])
const tableSearch = ref({
'Group by':[]
})
const tablePage = ref<any>({ currentPage: 1, pageSize: 100, total: 0 })
const tablePage = ref<any>({ currentPage: 1, pageSize: 50, total: 0 })
const tableSelect = ref<any>([])
const tableSort = ref({ column: '', order: '' })
const tableInfo = ref<any>({})
const timerObj = ref<any>({})
const numberRange = ref<string[]>([])
const dateRange=ref<string[]>([])
const searchReportSlots=ref<string[]>([])
const hideColumns=ref<string[]>([])
const dateTimeRange=ref<string[]>([])
const timeRange=ref<string[]>([])
const amountFieds=ref<any>({})
@@ -136,22 +175,76 @@ const crudRef = ref()
const dimensionFields=ref<any>({})
const exportLoading = ref(false)
const fieldList = ref<any[]>([]) // 添加fieldList引用
const hideFeilds= ref<any>({})
const hideCols=ref<any>([])
const dictFieldList=ref<any>([])
const permissions =
wsCache.get(CACHE_KEY.USER).lideeYunjipermissions?.[route.meta.menuDataId as string] || false
const selectIds = computed(() => {
return tableSelect.value.map((item) => item['id'])
})
const remoteMethod= async(query,column)=>{
if(!query){
return
}
let params={
pageSize: 100,
pageNo:1,
}
params[column.prop]=query
let data = await ReportApi.getTableList(column.reportCode,params)
column.dicData=column.formatterDic(data)
}
const crudBind = computed(() => {
const obj = {}
if (tableInfo.value.isPage) obj['page'] = tablePage.value
return obj
})
function getElementToBodyTop(el) {
if (!el || !(el instanceof HTMLElement)) return 0;
// 1. 获取元素相对于视口的位置top是元素顶部到视口顶部的距离
const rect = el.getBoundingClientRect();
// 2. 视口顶部到body顶部的距离 = 页面滚动的距离window.scrollY
// 最终距离 = 元素视口top + 页面滚动距离 - body的margin/padding可选根据场景
const bodyTop = document.body.getBoundingClientRect().top; // body自身的偏移通常为0
return rect.top - bodyTop + window.scrollY;
}
const tableHeight=ref(0)
watch(()=>tableHeightRef.value,()=>{
if(tableHeightRef.value){
setTimeout(()=>{
observeHeightChange(document.querySelector('.avue-crud__search'))
if(tableOption.value.showSummary&&tableOption.value.index&&tableOption.value.selection){
document.querySelector('tfoot tr td').colSpan="2"
document.querySelectorAll('tfoot tr td')[1].style.display="none"
}
},2000)
}
})
let resizeObserver = null;
const observeHeightChange = (targetEl) => {
// 确保元素已挂载
if (!targetEl) return;
// 创建ResizeObserver实例
resizeObserver = new ResizeObserver((entries) => {
const calcH= getElementToBodyTop(tableHeightRef.value)
tableHeight.value= ('calc(100vh - '+(parseInt(calcH+160))+"px)")
if(document.querySelector('.el-table').style){
document.querySelector('.el-table').style.height=tableHeight.value
}
});
// 开始监听目标元素
resizeObserver.observe(targetEl);
};
const setLastAmountRowClass=(row,rowIndex)=>{
if(row.isAmount===true){
return 'lastAmountRow'
}
}
const summaryMethod1=({columns,data})=>{
// 1. 初始化汇总结果数组(和表格列数量一致)
const result = new Array(columns.length).fill("");
@@ -164,66 +257,106 @@ const summaryMethod1=({columns,data})=>{
}
})
return result;
}
const rowStyleMethod=({row,rowIndex})=>{
let styleObj={}
dictFieldList.value.forEach(item=>{
if(row[item.prop]){
styleObj['color']=item.dicData.find(d=>d.value===row[item.prop]).cssClass
}
})
return styleObj
}
function getCurrentDate() {
const now = new Date();
// 补零函数小于10则前面加0
const padZero = (num) => num.toString().padStart(2, '0');
const year = now.getFullYear();
const month = padZero(now.getMonth() + 1);
const day = padZero(now.getDate());
// 返回格式化后的字符串或对象,按需选择
return {
year,
month,
day,
fullDate: `${year}-${month}-${day}` // 拼接成 2026-02-24 格式
};
}
const initTable = async () => {
isInit.value = false
loading.value = true
const { fieldList: apiFieldList, reportVo } = await ReportApi.getWebConfig(props.reportCode)
// 存储字段列表到响应式引用
fieldList.value = apiFieldList
const isHeight = reportVo.tableConfig?.includes('height')
const isPage = reportVo.dataConfig?.includes('page')
const isPermi = reportVo.dataConfig?.includes('authTrue')
const isHideExport = reportVo.tableConfig?.includes('hideExport')
hideColumns.value=[]
tableInfo.value = { ...reportVo, isPage, isHeight, isPermi, isHideExport }
tableOption.value = {
selection: !isHideExport,
selection: false,
reserveSelection: true,
menu: false,
addBtn: false,
height: isHeight ? 'auto' : undefined,
calcHeight: isHeight ? 160 : '',
calcHeight: isHeight ?10 : '',
index: reportVo.tableConfig.includes('index'),
indexLabel: '序号',
indexWidth: 54,
border: reportVo.tableConfig.includes('border'),
stripe: reportVo.tableConfig.includes('stripe'),
// stripe: reportVo.tableConfig.includes('stripe'),
showSummary:false,
stripe:true,
searchBtnText:'查询',
column: {}
}
//国际化处理
const fieldLengObj = assembleLengObj(apiFieldList, 'labelI18n', 'fieldCode', 'fieldName')
for (const key in fieldLengObj) {
mergeLocaleMessage(key, { [props.reportCode]: fieldLengObj[key] })
}
// 根据parentFieldCode判断表头结构
const hasSubFields = Array.isArray(apiFieldList) && apiFieldList.some(field => field.parentFieldCode && field.parentFieldCode !== '')
// 分离固定列和动态列
const fixedFields = apiFieldList.filter(f => f.isFixedColumn === 'Y')
const dynamicFields = apiFieldList.filter(f => f.isFixedColumn !== 'Y')
// 构建父子关系映射
const parentChildMap = new Map()
const childParentMap = new Map()
// 建立映射关系
apiFieldList.forEach(field => {
// 处理固定列
fixedFields.forEach((item) => {
const config: any = {
prop: item.fieldCode,
label: t(`${props.reportCode}.${item.fieldCode}`),
type: 'input',
overHidden: true,
isExport: item.isExport == 'Y',
search: item.queryIsWeb == 'Y'&&item.isHideSearch !== 'Y',
fixed: true
}
tableOption.value.column[item.fieldCode] = config
})
// 构建父子关系映射(仅用于识别子字段配置)
const childFieldConfigs = new Map()
dynamicFields.forEach(field => {
if (field.parentFieldCode && field.parentFieldCode !== '') {
// 这是子字段
childParentMap.set(field.fieldCode, field.parentFieldCode)
if (!parentChildMap.has(field.parentFieldCode)) {
parentChildMap.set(field.parentFieldCode, [])
if (!childFieldConfigs.has(field.parentFieldCode)) {
childFieldConfigs.set(field.parentFieldCode, [])
}
parentChildMap.get(field.parentFieldCode).push(field.fieldCode)
childFieldConfigs.get(field.parentFieldCode).push(field)
}
})
//字段处理 - 构建正确的表头结构
apiFieldList.forEach((item, index) => {
// 跳过子字段,子字段会在父字段中处理
if (item.parentFieldCode && item.parentFieldCode !== '') {
return
}
// let tableDicData=[]
// if( dynamicFields.filter(item=>['货品名称','剂型名称'].includes(item.fieldName)&&item.queryIsWeb == 'Y'&&item.isHideSearch !== 'Y')?.length){
// tableDicData =await ReportApi.getTableList(props.reportCode)
// }
// 处理动态列暂不构建children等数据返回后动态生成
dynamicFields.forEach(async (item, index) => {
if (item.parentFieldCode && item.parentFieldCode !== '') return
const config: any = {
prop: item.fieldCode,
label: t(`${props.reportCode}.${item.fieldCode}`),
@@ -231,60 +364,114 @@ const initTable = async () => {
overHidden: true,
isExport: item.isExport == 'Y',
sortable: item.isShowSort == 'Y' ? 'custom' : false,
search: item.queryIsWeb == 'Y',
search: item.queryIsWeb == 'Y'&&item.isHideSearch !== 'Y',
_hasChildConfig: childFieldConfigs.has(item.fieldCode),
_childConfigs: childFieldConfigs.get(item.fieldCode) || []
}
// 如果该字段有子字段,添加子列配置
if (parentChildMap.has(item.fieldCode)) {
const childFields = parentChildMap.get(item.fieldCode)
config.children = childFields.map(childFieldCode => {
const childField = apiFieldList.find(f => f.fieldCode === childFieldCode)
return {
prop: childField.fieldCode,
label: t(`${props.reportCode}.${childField.fieldCode}`),
type: 'input',
overHidden: true,
isExport: childField.isExport == 'Y',
sortable: childField.isShowSort == 'Y' ? 'custom' : false,
search: childField.queryIsWeb == 'Y'
if(item.queryIsWeb == 'Y'&&item.isHideSearch !== 'Y') isSearch.value=true
if(!!item.searchDefaultValue){
if(['年','年度','年份'].includes(item.fieldName)&&item.searchDefaultValue=='本年'){
tableSearch.value[config.prop]=getCurrentDate().year.toString()
}else if( ['月','月度','月份'].includes(item.fieldName)&&item.searchDefaultValue=='本月'){
tableSearch.value[config.prop]=new Date().getMonth() + 1
}else if(item.fieldType=='Date'&&item.searchDefaultValue=='当日'){
tableSearch.value[config.prop]=getCurrentDate().fullDate
}else{
tableSearch.value[config.prop]=item.searchDefaultValue
}
})
}
if(['年','年度','年份','月','月度','月份','年月'].includes(item.fieldName)&&config.search){
config.searchSpan=3
if(item.fieldName.includes('年')){
config.searchType='year'
config.valueFormat = 'YYYY'
}else if(item.fieldName.includes('月')){
config.searchType='select'
config.dicData=[{label:'1月',value:'1'},{label:'2月',value:'2'},{label:'3月',value:'3'},{label:'4月',value:'4'},{label:'5月',value:'5'},{label:'6月',value:'6'},{label:'7月',value:'7'},{label:'8月',value:'8'},{label:'9月',value:'9'},{label:'10月',value:'10'},{label:'11月',value:'11'},{label:'12月',value:'12'}]
}
if(props.reportCode=='ZHXSQK'&&item.isDimension=='Y'){
tableSearch.value['Group by'].push(item.fieldCode)
}
}
if(item.fieldName=='效期剩余时间(天)分类') config.searchSpan=5
if(config.search) config.searchLabelWidth=item.fieldName.length*12 +40
const moreIn="MORE_IN".includes(item.queryMode)
config.searchType=moreIn?'select':config.searchType
config.multiple=config.collapseTags=config.collapseTagsTooltip =moreIn
item.queryMode=='LIKE'?config.multiple=config.collapseTags=config.collapseTagsTooltip =true:''
const jkTableDicFields={
'货品名称':'hpmx',
'货品':'hpmx',
'剂型':'jxjk',
'产品':'hpmx',
'产品名称':'hpmx',
'剂型名称':'jxjk',
'客户名称':'khxx',
'销售类型':'xslx',
'客户':'khxx',
'业务员':'ywyxx',
'业务区域':'ywqy',
}
if(Object.keys(jkTableDicFields).includes(item.fieldName)&&config.search){
config.dataType= 'string'
config.reportCode= `${jkTableDicFields[item.fieldName]}`
const formatterData=(res) => { //请求数据格式化
const arr=[...new Set(res.records.map(item=>item[config.prop=="zonename"?"salezonename":config.prop]))]
return arr.map(item=>({label:item,value:item}))
}
let data = await ReportApi.getTableList(jkTableDicFields[item.fieldName],{pageSize: 50, pageNo:1})
config.formatterDic=formatterData
config.page=1
config.total=data.total
config.dicData=formatterData(data)
searchReportSlots.value.push(config.prop)
// config.multiple?tableSearch.value[config.prop]=item.searchDefaultValue?item.searchDefaultValue.split(','):[]:''
}
if(item.dictCode){
const dictData=getStrDictOptions(item.dictCode)
config.dicData=dictData
config.searchType= 'select'
config.dataType= 'string'
dictFieldList.value.push(config)
}
if(!!item.isAmount){
index==0?amountFieds.value.fistField=config.prop:''
amountFieds.value[item.isAmount]=config
tableOption.value.showSummary=true
}
if(item.width){
config.width=item.width
config.widthOld=item.width
}
if(item.isDimension=='Y'){
dimensionFields.value[config.prop]=config
hideFeilds.value[config.prop]=item.isHideDimension
}
if(item.isHideCol == 'Y') {hideCols.value.push(config.prop)
config.hide = true
hideColumns.value.push(config.prop)
}
if (item.queryMode == 'RANGE') config.searchRange = true
if (['Integer', 'BigInt', 'BigDecimal'].includes(item.fieldType)) config.type = 'number'
else if (item.fieldType == 'Date') {
config.type = 'date'
config.format = 'YYYY-MM-DD'
config.valueFormat = 'YYYY-MM-DD'
if(config.searchRange){
dateRange.value.push(config.prop)
}
if(config.searchRange) dateRange.value.push(config.prop)
} else if (item.fieldType == 'Time') {
config.type = 'time'
config.format = 'HH:mm:ss'
config.valueFormat = 'HH:mm:ss'
if(config.searchRange){
timeRange.value.push(config.prop)
}
if(config.searchRange) timeRange.value.push(config.prop)
} else if (item.fieldType == 'DateTime') {
config.type = 'datetime'
config.format = 'YYYY-MM-DD HH:mm:ss'
config.valueFormat = 'YYYY-MM-DD HH:mm:ss'
if(config.searchRange){
dateTimeRange.value.push(config.prop)
}
if(config.searchRange) dateTimeRange.value.push(config.prop)
}
if (config.type == 'number' && config.searchRange) numberRange.value.push(config.prop)
@@ -292,15 +479,27 @@ const initTable = async () => {
config.searchType = 'tag'
}
if (item.queryMode == 'NE') config.searchLabel = `${config.label} !=`
tableOption.value.column[item.fieldCode] = config
if(!item.parentFieldCode&&!!item.parentFieldName){
if(tableOption.value.column[item.parentFieldName]){
tableOption.value.column[item.parentFieldName].children.push(config)
}else{
tableOption.value.column[item.parentFieldName]={
children:[config],
label:item.parentFieldName
}
}
}else{
tableOption.value.column[item.fieldCode] = config
}
})
isInit.value = true
searchChange()
initTableLayout()
}
const searchDimension=()=>{
searchChange()
}
const initTableLayout = () => {
@@ -308,7 +507,7 @@ const initTableLayout = () => {
if (tableInfo.value.isHeight || props.calcHeight) {
const calcH = props.calcHeight
let calcHeight = calcH || 160
let calcHeight = calcH || 10
if (calcH) tableOption.value.height = 'auto'
tableOption.value.calcHeight = calcHeight
}
@@ -317,6 +516,7 @@ const initTableLayout = () => {
timerObj.value.layout = setTimeout(() => {
if (crudRef.value) crudRef.value.getTableHeight()
}, 100)
}
const selectionChange = (data) => {
@@ -366,60 +566,210 @@ const getTableData = async (isLoading = true) => {
if (isLoading) loading.value = true
const searchObj = await getSearchData()
try {
const data = await ReportApi.getTableList(props.reportCode, searchObj)
// 获取数据
let data = await ReportApi.getTableList(props.reportCode, searchObj)
// 功能测试CS_DTBT报表使用模拟数据
if (props.reportCode === 'CS_DTBT') {
data = {
records: [
{ "yuefen": "一月", "benyue": 100, "leiji": 110 },
{ "yuefen": "一月", "benyue": 120, "leiji": 130 },
{ "yuefen": "二月", "benyue": 140, "leiji": 150 },
{ "yuefen": "二月", "benyue": 160, "leiji": 170 },
{ "yuefen": "三月", "benyue": 180, "leiji": 190 },
{ "yuefen": "三月", "benyue": 200, "leiji": 210 },
{ "yuefen": "四月", "benyue": null, "leiji": null },
{ "yuefen": "四月", "benyue": 220, "leiji": 230 },
{ "yuefen": "五月", "benyue": 333, "leiji": 444 },
{ "yuefen": "六月", "benyue": 555, "leiji": 555 },
{ "yuefen": "六月", "benyue": 666, "leiji": 666 },
],
total: 6
}
}
if (tablePage.value) tablePage.value['total'] = data.total
// 处理包含子字段的数据
let processedData = data.records
// 根据字段配置判断是否需要处理子字段
const hasSubFields = Array.isArray(fieldList.value) && fieldList.value.length > 0 &&
fieldList.value.some(field => field.parentFieldCode && field.parentFieldCode !== '')
// 查找动态分组字段(用于生成一级表头)
const groupField = fieldList.value.find(f => f.isDynamicGroup === 'Y')
if (hasSubFields) {
processedData = data.records.map(record => {
const flatRecord = { ...record }
// 处理子字段数据
fieldList.value.forEach(field => {
if (field.parentFieldCode && field.parentFieldCode !== '') {
// 这是子字段,从父字段中提取数据
const parentData = record[field.parentFieldCode]
if (parentData && typeof parentData === 'object') {
flatRecord[field.fieldCode] = parentData[field.fieldCode]
}
// 动态生成二维表头
if (data.records && data.records.length > 0) {
const newColumns = {}
// 保留固定列
for (const colKey in tableOption.value.column) {
const colConfig = tableOption.value.column[colKey]
if (colConfig.fixed) {
newColumns[colKey] = colConfig
}
}
if (groupField) {
// 有分组字段,从数据中提取所有不重复的分组值(一级表头)
const groupValues = [...new Set(data.records.map(r => r[groupField.fieldCode]))].filter(Boolean)
// 找到所有子字段配置按sortNum排序
const childFields = fieldList.value
.filter(f =>
f.parentFieldCode &&
f.parentFieldCode !== '' &&
f.isFixedColumn !== 'Y'
)
.sort((a, b) => (a.sortNum || 0) - (b.sortNum || 0))
// 为每个分组值生成列
groupValues.forEach(groupValue => {
const parentProp = `group_${groupValue}`
newColumns[parentProp] = {
label: groupValue,
children: childFields.map(childField => ({
prop: `${parentProp}_${childField.fieldCode}`,
label: t(`${props.reportCode}.${childField.fieldCode}`),
type: ['Integer', 'BigInt', 'BigDecimal'].includes(childField.fieldType) ? 'number' : 'input',
overHidden: true,
isExport: childField.isExport == 'Y'
}))
}
})
} else {
// 没有分组字段,使用原有的逻辑
for (const colKey in tableOption.value.column) {
const colConfig = tableOption.value.column[colKey]
if (!colConfig.fixed) {
newColumns[colKey] = colConfig
}
}
}
tableOption.value.column = newColumns
}
// 将扁平数据按分组字段转换为二维表头数据
let processedData = []
if (groupField && data.records.length > 0) {
// 有分组字段,根据分组字段值进行分组
const fixedFields = fieldList.value.filter(f => f.isFixedColumn === 'Y')
const childFields = fieldList.value
.filter(f => f.parentFieldCode && f.parentFieldCode !== '' && f.isFixedColumn !== 'Y')
.sort((a, b) => (a.sortNum || 0) - (b.sortNum || 0))
// 获取所有分组值
const groupValues = [...new Set(data.records.map(r => r[groupField.fieldCode]))].filter(Boolean)
// 按分组字段值对数据进行分组
const groupedData = {}
data.records.forEach(record => {
const groupValue = record[groupField.fieldCode]
if (!groupedData[groupValue]) {
groupedData[groupValue] = []
}
groupedData[groupValue].push(record)
})
// 计算需要生成的行数(根据固定列配置或分组内最大数据量)
let rowCount = 1
if (fixedFields.length > 0 && fixedFields[0].fixedColumnValue) {
rowCount = fixedFields[0].fixedColumnValue.split(',').length
} else {
// 如果没有固定列,取每个分组中数据最多的数量
rowCount = Math.max(...Object.values(groupedData).map((arr: any[]) => arr.length))
}
// 按行处理数据
for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
const flatRecord = {}
// 处理固定列
fixedFields.forEach(field => {
if (field.fixedColumnValue) {
const fixedValues = field.fixedColumnValue.split(',')
flatRecord[field.fieldCode] = fixedValues[rowIndex] || ''
}
})
// 处理动态列数据(根据分组字段值获取对应数据)
groupValues.forEach(groupValue => {
const groupRecords = groupedData[groupValue] || []
const record = groupRecords[rowIndex]
if (record) {
const parentProp = `group_${groupValue}`
childFields.forEach(childField => {
flatRecord[`${parentProp}_${childField.fieldCode}`] = record[childField.fieldCode]
})
}
})
processedData.push(flatRecord)
}
} else {
// 没有分组字段,直接处理数据
processedData = data.records.map((record, recordIndex) => {
const flatRecord = { ...record }
// 处理固定列
fieldList.value.forEach(field => {
if (field.isFixedColumn === 'Y' && field.fixedColumnValue) {
const fixedValues = field.fixedColumnValue.split(',')
flatRecord[field.fieldCode] = fixedValues[recordIndex] || ''
}
})
return flatRecord
})
}
tableData.value = processedData
// amountFieds.value={
// 'thissaqty_s':{
// prop:'thissaqty',
// },
// "monthsaqty_s":{
// prop:'monthsaqty',
//},
//'lastsaqty_s':{
// prop:'lastsaqty'
//}
//}
//tableData.value[0]['thissaqty_s']=6083195687
// tableData.value[0]['monthsaqty_s']=972705058
// tableData.value[0]['lastsaqty_s']=6075936725
tableData.value = processedData
Object.values(tableOption.value.column).forEach( item=>{
let oldHide=hideCols.value.indexOf(item.prop)!==-1
item.hide=tableData.value[0]&&((tableSearch.value['Group by']&&tableData.value[0][item.prop]===undefined&&!item.children)||(item.children?.every(child=>(!['yearplan','yearrate'].includes(child.prop))&&(tableData.value[0][child.prop]===undefined))))||oldHide
const prH=hideColumns.value.indexOf(item.prop)
item.hide?(prH===-1?hideColumns.value.push(item.prop):''):(prH!==-1&&hideCols.value.indexOf(item.prop)===-1?hideColumns.value.splice(prH,1):'')
})
let keys=Object.keys(amountFieds.value)
if(!!keys.length&&!!tableData.value.length){
let obj={isAmount:true}
let value={...tableData.value[0]}
if(tableInfo.value.reportCode==='ZHXSQK'){
value=(await ReportApi.getAggMonthTotalDetail(searchObj))[0]
// searchObj['usemonth']?'':value.thisMonthSaMoney_s=value.thissamoney_s
}
keys.forEach(item=>{
let key=amountFieds.value[item].prop
key!=='fistField'?obj[key]=tableData.value[0][item]:''
key!=='fistField'?obj[key]=value[item]:''
})
amountObj.value=obj
}
let field=tableSearch.value['Group by']
let hides=[]
if(field.length){
field.forEach(item=>{
hides=Object.keys(hideFeilds.value).length?hideFeilds.value[item].split(','):[]
})
}
Object.keys(tableOption.value.column).forEach(key=>{
let item=tableOption.value.column[key]
item.hide=hides.includes(item.prop)||hideColumns.value.includes(item.prop)
})
Object.keys(tableOption.value.column).forEach(key=>{
const showCols=Object.values(tableOption.value.column).filter(item=>!item.hide)
let item=tableOption.value.column[key]
item.children?'':(item.width=showCols.length<14?undefined:item.widthOld)
item.children?.forEach(child=>{
child.width=showCols.length<14?undefined:child.widthOld
})
})
resolve(data.records)
} finally {
if (isLoading) loading.value = false
resolve()
}
})
}
@@ -464,11 +814,11 @@ const clearSearch = () => {
}
}
const searchChange = (params?, done?) => {
const searchChange = async (params?, done?) => {
if (tablePage.value) tablePage.value['currentPage'] = 1
getTableData().finally(() => {
if (done) done()
})
await getTableData()
if (done) done()
}
const resetChange = () => {
return new Promise(async (resolve) => {
@@ -525,14 +875,47 @@ defineExpose({
</script>
<style lang="scss" scoped>
:deep(.el-form-item--default){
margin-bottom: 10px;
}
:deep(.avue-crud__pagination){
padding-bottom: 0;
}
:deep(.avue-crud__tip){
display: none;
}
.amountBox{
&::after{
content:'、'
}
&:last-of-type::after{
content:''
}
}
:deep(.el-table){
.is-group th.el-table__cell,th.el-table__cell{
background-color: #C6EFFE;
}
.el-table__body tr.hover-row>td.el-table__cell{
background-color: #FFFFD2;
}
&.el-table--enable-row-hover .el-table__body tr:hover>td.el-table__cell{
background-color: #FFFFD2;
}
td.el-table__cell,th.el-table__cell{
border-bottom: 1px solid #000 !important;
border-right: 1px solid #000 !important;
}
--el-table-border-color: #000;
tfoot .el-table__cell{
border-top: 1px solid #000 !important;
}
}
.lastAmountRow{
}

View File

@@ -118,7 +118,7 @@ const getTableData = async () => {
})
if (userIds.length) {
const dicRes = await DicApi.getDicTableText({
lideeYunji_dictLabel: encryptAES(JSON.stringify([{ userIdList: [...new Set(userIds)] }]))
lideeYunJi_dictLabel: encryptAES(JSON.stringify([{ userIdList: [...new Set(userIds)] }]))
})
if (dicRes?.userList) {
dicRes.userList.forEach((item) => (userObj[item.id] = item.nickname))

View File

@@ -624,7 +624,7 @@ export const formDataFormatting = (formOption, formData, formType) => {
}
if (dicApiData.length) {
DicApi.getDicTableText({
lideeYunji_dictLabel: encryptAES(JSON.stringify(dicApiData))
lideeYunJi_dictLabel: encryptAES(JSON.stringify(dicApiData))
}).then(dicData => {
const dictData = {
userList: { dicKey: 'userSelect', label: 'nickname' },

View File

@@ -24,7 +24,7 @@ export const setUserAndDeptName = (params) => {
userList: { dicKey: 'userSelect', label: 'nickname' },
deptList: { dicKey: 'deptSelect', label: 'name' }
}
getDicTableText({ lideeYunji_dictLabel: encryptAES(JSON.stringify(dictLabel)) }).then(
getDicTableText({ lideeYunJi_dictLabel: encryptAES(JSON.stringify(dictLabel)) }).then(
(dicData) => {
for (const key in dicData) {
const dicObj = {}

View File

@@ -886,7 +886,7 @@ export const tableFormatting = (data, column, otherData: any = {}) => {
})
//查询回显文本
const dicApiData: any = { lideeYunji_dictLabel: [] }
const dicApiData: any = { lideeYunJi_dictLabel: [] }
if (dicTableData.keyList.length) {
const dicTableApiData: Array<string> = []
dicTableData.keyList.forEach(key => {
@@ -899,17 +899,17 @@ export const tableFormatting = (data, column, otherData: any = {}) => {
dicTableData[key].dataList = [...new Set(dicTableData[key].dataList)].filter((id: string) => !lowStore.dicObj[key] ? true : !lowStore.dicObj[key][id])
if (dicTableData[key].dataList.length) dicTableApiData.push(dicTableData[key])
})
dicApiData.lideeYunji_dictLabel.push(...dicTableApiData)
dicApiData.lideeYunJi_dictLabel.push(...dicTableApiData)
}
for (const key in userAndDeptControl.typeKey) {
const abbr = userAndDeptControl.typeKey[key]
userAndDeptControl[`${abbr}Ids`] = userAndDeptControl[`${abbr}Ids`].filter(id => {
return /^(\d+)$/.test(id + '')
})
if (userAndDeptControl[`${abbr}Ids`].length) dicApiData.lideeYunji_dictLabel.push({ [`${abbr}IdList`]: [...new Set(userAndDeptControl[`${abbr}Ids`])] })
if (userAndDeptControl[`${abbr}Ids`].length) dicApiData.lideeYunJi_dictLabel.push({ [`${abbr}IdList`]: [...new Set(userAndDeptControl[`${abbr}Ids`])] })
}
if (dicApiData.lideeYunji_dictLabel.length) {
dicApiData.lideeYunji_dictLabel = encryptAES(JSON.stringify(dicApiData.lideeYunji_dictLabel))
if (dicApiData.lideeYunJi_dictLabel.length) {
dicApiData.lideeYunJi_dictLabel = encryptAES(JSON.stringify(dicApiData.lideeYunJi_dictLabel))
const dictData = {
userList: { dicKey: 'userSelect', label: 'nickname', value: 'id' },
deptList: { dicKey: 'deptSelect', label: 'name', value: 'id' }

View File

@@ -310,7 +310,7 @@ const elementHover = (element) => {
<p>部门:${processInstance.value.startUser.deptName}</p>
<p>创建时间:${formatDate(processInstance.value.createTime)}`
} else if (element.value.type === 'bpmn:UserTask') {
// debugger
let task = taskList.value.find((m) => m.id === activity.taskId) // 找到活动对应的 taskId
if (!task) {
return

View File

@@ -49,7 +49,7 @@ export default defineComponent({
name: 'Layout',
setup() {
return () => (
<section class={[prefixCls, `${prefixCls}__${layout.value}`, 'w-[100%] h-[100%] relative']}>
<section class={[prefixCls, `${prefixCls}__${layout.value}`, 'w-[100%] h-[100%] relative']} >
{mobile.value && !collapse.value ? (
<div
class="absolute left-0 top-0 z-99 h-full w-full bg-[var(--el-color-black)] opacity-30"

View File

@@ -42,6 +42,7 @@ const formatComponentInstance = (component, route) => {
<template>
<section
style="padding-bottom:0"
:class="[
'flex-1 p-[var(--app-content-padding)] w-[calc(100%-var(--app-content-padding)-var(--app-content-padding))] bg-[var(--app-content-bg-color)] dark:bg-[var(--el-bg-color)]'
]"

View File

@@ -69,7 +69,7 @@ export default {
noPermission: `抱歉,您无权访问此页面。`,
pageError: '抱歉,您访问的页面不存在。',
networkError: '抱歉,服务器报告错误。',
returnToHome: '返回首页'
returnToHome: '返回工作台'
},
permission: {
hasPermission: `请设置操作权限标签值`,
@@ -157,7 +157,7 @@ export default {
router: {
login: '登录',
socialLogin: '社交登录',
home: '首页',
home: '工作台',
analysis: '分析页',
workplace: '工作台'
},
@@ -275,7 +275,7 @@ export default {
},
exception: {
backLogin: '返回登录',
backHome: '返回首页',
backHome: '返回工作台',
subTitle403: '抱歉,您无权访问此页面。',
subTitle404: '抱歉,您访问的页面不存在。',
subTitle500: '抱歉,服务器报告错误。',
@@ -292,7 +292,7 @@ export default {
},
login: {
backSignIn: '返回',
signInFormTitle: '登录',
signInFormTitle: '国瑞药业工业互联网平台',
ssoFormTitle: '三方授权',
mobileSignInFormTitle: '手机登录',
qrSignInFormTitle: '二维码登录',

View File

@@ -9,7 +9,7 @@ import { useDictStoreWithOut } from '@/store/modules/dict'
import { useUserStoreWithOut } from '@/store/modules/user'
import { useLowStoreWithOut } from '@/store/modules/low'
import { usePermissionStoreWithOut } from '@/store/modules/permission'
import * as authUtil from '@/utils/auth'
const { start, done } = useNProgress()
const { loadStart, loadDone } = usePageLoading()
@@ -63,7 +63,24 @@ router.beforeEach(async (to, from, next) => {
loadStart()
if (getAccessToken()) {
if (to.path === '/login') {
next({ path: '/' })
const queryParams = new URLSearchParams(window.location.search);
const ticketUrl = queryParams.get('url');
const ssUrl = queryParams.get('ssUrl');
if(ticketUrl!=null&&ssUrl==null){
next()
}else if(ticketUrl!=null&&ssUrl!==null){
const accessToken =authUtil.getAccessToken();
if(accessToken!=null){
const urll=ticketUrl+"?ticket="+accessToken;
window.location.href = urll+(ssUrl?"&redirect="+ssUrl:'');
}else{
next({ path: '/' })
}
}else{
next({ path: '/' })
}
} else {
// 获取所有字典
const dictStore = useDictStoreWithOut()

View File

@@ -26,7 +26,7 @@ export const setupAvue = async (app: App<Element>) => {
searchSpan: 6,
searchMenuSpan: 6,
searchMenuPosition: 'left',
searchIndex: 3,
searchIndex: 4,
searchIcon: true,
searchShowBtn: true,
labelSuffix: ' ',

View File

@@ -95,7 +95,9 @@
display: flex;
justify-content: flex-start;
}
.width-50{
width: 50% !important;
}
/* nprogress 适配 element-plus 的主题色 */
#nprogress {
& .bar {

View File

@@ -50,9 +50,11 @@
--app-content-bg-color: #f5f7f9;
--app-footer-height: 50px;
--app-footer-height: 30px;
--transition-time-02: 0.2s;
--el-fill-color-lighter: #F5F5F6;
}
.dark {

View File

@@ -2,7 +2,7 @@ import dayjs from 'dayjs'
import type { TableColumnCtx } from 'element-plus'
/**
* 日期快捷选项适用于 el-date-picker
* 日期快捷选项适用于 -picker
*/
export const defaultShortcuts = [
{

View File

@@ -89,9 +89,6 @@ export default {
appsData: []
};
},
mounted() {
this.fetchApps()
},
computed: {
categorizedApps() {
const result = {};
@@ -135,6 +132,9 @@ export default {
return Object.keys(this.filteredCategories).length;
}
},
mounted() {
this.fetchApps()
},
methods: {
async fetchApps() {
try {
@@ -155,7 +155,8 @@ export default {
handleAppClick(app) {
const uri = Array.isArray(app.redirectUris) ? app.redirectUris[0] : app.redirectUris
if (uri) {
window.location.href = uri
window.open(uri,'_blank');
// window.location.href = uri
} else {
this.$message.warning('未配置重定向地址')
}

View File

@@ -1,107 +1,45 @@
<template>
<div :class="prefixCls" class="relative h-[100%]">
<div class="relative mx-auto h-full flex">
<div :class="prefixCls" class="relative h-[100%]" :style="{ backgroundImage: 'url(' + localImage + ')' ,backgroundSize:'cover'}">
<div class="relative mx-auto h-full flex" style=" margin: 0 auto;">
<div
v-if="!appStore.getMobile"
:class="`${prefixCls}__left flex-1 bg-#409eff dark:bg-[var(--login-bg-color)] relative p-30px lt-xl:hidden overflow-x-hidden overflow-y-auto`"
>
<!-- 左上角的 logo + 系统标题 -->
<div
class="relative flex items-center text-white mt-30px"
:style="{ marginTop: leftStyle.logo.mt }"
>
<img
alt=""
class="mr-10px"
:style="{ height: leftStyle.logo.height }"
src="@/assets/imgs/logo_white.png"
/>
</div>
<!-- 左边的背景图 + 欢迎语 -->
<Transition
appear
enter-active-class="animate__animated animate__bounceInLeft"
tag="div"
>
<div :style="{ marginTop: leftStyle.bottomText.textMt }">
<div
class="pos-relative w-100% overflow-hidden"
:style="{ height: leftStyle.lottie.boxHeight }"
>
<vue3-lottie
class="pos-absolute"
:style="{ left: leftStyle.lottie.left, top: leftStyle.lottie.top }"
:animation-data="loginAnimationData"
:height="leftStyle.lottie.height"
:width="leftStyle.lottie.width"
:auto-play="true"
:loop="true"
/>
</div>
<div
class="flex justify-center w-100%"
:style="{ marginTop: leftStyle.bottomText.textMt }"
>
<div
class="pos-relative overflow-hidden"
:style="{ width: leftStyle.bottomText.width, height: leftStyle.bottomText.height }"
>
<img
alt=""
class="pos-absolute top-0"
:style="{ width: leftStyle.bottomText.width, left: leftStyle.bottomText.left }"
src="@/assets/imgs/login/login_text.png"
/>
</div>
</div>
</div>
</Transition>
</div>
<div
class="relative flex-1 p-30px bg-#e3f0ff lt-sm:p-10px overflow-x-hidden overflow-y-auto"
:class="`${prefixCls}__right`"
>
<!-- 右上角的主题语言选择 -->
<div
class="relative z-2 flex items-center justify-between text-white at-2xl:justify-end at-xl:justify-end"
>
<div class="flex items-center at-2xl:hidden at-xl:hidden">
<img alt="" class="mr-10px h-32px" src="@/assets/imgs/logo.png" />
</div>
<div class="flex items-center justify-end space-x-10px h-48px">
<ThemeSwitch />
<LocaleDropdown class="dark:text-white lt-xl:text-white" />
</div>
</div>
class="relative flex-1 p-30px lt-sm:p-10px overflow-x-hidden overflow-y-auto"
:class="`${prefixCls}__right`">
<!-- 右边的登录界面 -->
<Transition appear enter-active-class="animate__animated animate__bounceInRight">
<div
:class="newClass"
class="pos-relative z-2 m-auto h-[calc(100%-60px)] w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px"
class="pos-relative z-2 m-auto h-[calc(100%-200px)] w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px"
>
<div class="bg-#fff dark:bg-[var(--login-bg-color)] b-rounded-20px px-20px">
<!-- 账号登录 -->
<LoginForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
<!-- 手机登录 -->
<MobileForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
<!-- 二维码登录 -->
<QrCodeForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
<!-- 三方登录 -->
<!-- <SSOLoginVue class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" /> -->
</div>
</div>
</Transition>
</div>
<div
class="pos-fixed bottom-0px left-50% lt-md:left-0px lt-sm:left-0px lt-xl:left-0px lt-xl:left-0px z-1"
>
<img class="w-138px block" src="@/assets/imgs/login/login_right_bg_1.png" alt="" />
</div>
<div class="pos-fixed right--38px top--23px">
<img class="w-160px block" src="@/assets/imgs/login/login_right_bg_2.png" alt="" />
</div>
</div>
<div class="tags">
<div class="module cockpit">
<img :src="jsc"/>
驾驶舱
</div>
<div class="module iot">
<img :src="wlzt"/>
物联中台
</div>
<div class="module data-center">
<img :src="sjzt"/>
数据中台
</div>
<div class="module report">
<img :src="znbb"/>
智能报表
</div>
<div class="module digital-twin">
<img :src="szls"/>
数字孪生
</div>
</div>
</div>
</template>
<script lang="ts" setup>
@@ -110,9 +48,16 @@ import { useAppStore } from '@/store/modules/app'
import { ThemeSwitch } from '@/layout/components/ThemeSwitch'
import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
import { useWindowSize } from '@vueuse/core'
import { LoginForm, MobileForm, QrCodeForm, SSOLoginVue } from './components'
import { LoginForm } from './components'
import * as loginAnimation from '@/assets/json/login_left.json'
import jsc from '@/assets/imgs/jsc.png'
import sjzt from '@/assets/imgs/sjzt.png'
import szls from '@/assets/imgs/szls.png'
import wlzt from '@/assets/imgs/wlzt.png'
import znbb from '@/assets/imgs/znbb.png'
// import { useRoute } from 'vue-router';
import localImage from '@/assets/imgs/background.jpg'
defineOptions({ name: 'Login' })
@@ -148,7 +93,6 @@ const leftStyle = computed(() => {
linkMT: leftW / 23.87 + 'px',
textMt: leftW / 16.2 + 'px'
}
return { lottie, bottomText, logo }
})
@@ -157,11 +101,14 @@ const newClass = computed(() => {
const toggle = appStore.getFullscreen ? 'is-toggle' : ''
return toggle ? toggle : mobile
})
</script>
<style lang="scss" scoped>
$prefix-cls: #{$namespace}-login;
.bg-\#fff{
--un-bg-opacity: 0.3;
}
.#{$prefix-cls} {
overflow: auto;
}
@@ -179,4 +126,78 @@ $prefix-cls: #{$namespace}-login;
left: 50%;
transform: translate(-50%, -50%) scale(0.7, 0.6);
}
.tags{
position:absolute;
bottom:20px;
left:50%;
display: grid;
width: 760px;
transform:translateX(-50%);
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: auto auto;
gap: 20px;
.module {
display: flex;
padding: 16px;
font-size: 22px;
font-weight: 600;
color: #fff;
text-align: center;
border-radius: 4px;
align-items: center;
justify-content: center;
flex-direction: column;
}
.module img {
width: 52px;
height: 52px;
margin-bottom: 16px;
object-fit: contain;
}
.module.iot,
.module.data-center,
.module.report,
.module.digital-twin {
flex-direction: row;
gap: 32px; /* 图标和文字之间的间距 */
img{
margin:0
}
}
/* 驾驶舱:深蓝色渐变 */
.module.cockpit {
grid-row: 1 / 3;
background: linear-gradient(180deg, rgb(109 147 245 / 75%), rgb(7 38 111 / 75%));
img {
width: 96px;
height: 96px;
}
}
/* 物联中台:中蓝色渐变 */
.module.iot {
background: linear-gradient(180deg, rgb(130 156 223 / 75%), rgb(7 38 111 / 75%));
}
/* 数据中台:青绿色渐变 */
.module.data-center {
background: linear-gradient(180deg, rgb(46 192 182 / 75%), rgb(40 180 170 / 75%));
}
/* 智能报表:绿色渐变 */
.module.report {
background: linear-gradient(180deg, rgb(77 212 122 / 75%), rgb(60 180 100 / 75%));
}
/* 数字孪生:浅蓝色渐变 */
.module.digital-twin {
background: linear-gradient(180deg, rgb(111 181 235 / 75%), rgb(60 140 200 / 75%));
}
}
</style>

View File

@@ -82,7 +82,6 @@
mode="pop"
@success="handleLogin"
/>
</el-row>
</el-form>
</template>
@@ -97,6 +96,7 @@ import * as authUtil from '@/utils/auth'
import { usePermissionStore } from '@/store/modules/permission'
import * as LoginApi from '@/api/login'
import { LoginStateEnum, useFormValid, useLoginState } from './useLogin'
import { create } from 'domain'
defineOptions({ name: 'LoginForm' })
@@ -140,42 +140,6 @@ const loginData = reactive({
}
})
const socialList = [
{ icon: 'ant-design:wechat-filled', type: 30 },
{ icon: 'ant-design:dingtalk-circle-filled', type: 20 },
{ icon: 'ant-design:github-filled', type: 0 },
{ icon: 'ant-design:alipay-circle-filled', type: 0 }
]
const easyDropdownList = [
{
label: t('login.btnMobile'),
icon: 'ant-design:phone-twotone',
type: LoginStateEnum.MOBILE,
code: 'page'
},
{
label: t('login.btnQRCode'),
icon: 'ant-design:qrcode-outlined',
type: LoginStateEnum.QR_CODE,
code: 'page'
},
{ label: t('login.btnWechat'), icon: 'ant-design:wechat-filled', type: 30, code: 'url' },
{
label: t('login.btnDingtalk'),
icon: 'ant-design:dingtalk-circle-filled',
type: 20,
code: 'url'
},
{ label: t('login.btnGitHub'), icon: 'ant-design:github-filled', type: 0, code: 'url' },
{ label: t('login.btnAlipay'), icon: 'ant-design:alipay-circle-filled', type: 0, code: 'url' }
]
const easyCommand = (row) => {
if (row.code == 'page') setLoginState(row.type)
else if (row.code == 'url') doSocialLogin(row.type)
}
// 获取验证码
const getCode = async () => {
// 情况一,未开启:则直接登录
@@ -207,6 +171,7 @@ const getCookie = () => {
}
}
}
// 根据域名,获得租户信息
const getTenantByWebsite = async () => {
const website = location.host
@@ -221,7 +186,6 @@ const loading = ref() // ElLoading.service 返回的实例
const handleLogin = async (params) => {
loginLoading.value = true
try {
const data = await validForm()
if (!data) return
@@ -242,6 +206,7 @@ const handleLogin = async (params) => {
authUtil.removeLoginForm()
}
authUtil.setToken(res)
if (!redirect.value) {
redirect.value = '/'
}
@@ -249,7 +214,23 @@ const handleLogin = async (params) => {
if (redirect.value.indexOf('sso') !== -1) {
window.location.href = window.location.href.replace('/login?redirect=', '')
} else {
push({ path: redirect.value || permissionStore.addRouters[0].path })
const queryParams = new URLSearchParams(window.location.search);
const ticketUrl = queryParams.get('url'); // 获取查询参数值,例如 ?paramName=value 中的 value
const accessToken =authUtil.getAccessToken();
console.log(accessToken);
if(ticketUrl!=null){
var urll=ticketUrl+"?ticket="+accessToken;
var ssUrl=queryParams.get('ssUrl')
window.location.href = urll+(ssUrl?"&redirect="+ssUrl:'');
}else{
push({ path: redirect.value || permissionStore.addRouters[0].path })
}
}
} finally {
loginLoading.value = false

View File

@@ -74,6 +74,75 @@ export const useRenderVxeColumn = (useType = 'table') => {
return <el-input class="my-cell" text="text" v-model={row[prop]} placeholder={placeholder ? placeholder : '请输入 ' + column.title} />
}
},
LowInputDefualt: {
default: (renderOpts, { row, column, fieldProp }, isStop) => {
const prop = fieldProp || column.field
if (isStop) return (<div> <span>{row[prop]}</span> {stopIcon} </div>)
return <span>{row[prop]}</span>
},
edit: (renderOpts, { row, column, fieldProp ,rowIndex}) => {
const { placeholder } = renderOpts
const prop = fieldProp || column.field
const dicData=[]
if(['年','年度','年份'].includes(row.fieldName)){
dicData.push({label:'本年',value:'本年'})
dicData.push({label:'指定年份',value:'指定'})
}else if( ['月','月度','月份'].includes(row.fieldName)){
dicData.push({label:'本月',value:'本月'})
dicData.push({label:'指定月份',value:'指定'})
}else if(row.fieldType=='Date'){
dicData.push({label:'当日',value:'当日'})
dicData.push({label:'指定日期',value:'指定'})
}
if(dicData.length){
return [
<avue-select
popper-class="vxe-table--ignore-clear"
v-model={row.defaultSelect}
placeholder={'请选择'}
class={row.defaultSelect=="指定"?'width-50':''}
dic={dicData}
clearable={true}
onChange={({value}) => {
if(value!=='指定') row[prop]=value
else row[prop]=''
// 再触发自定义事件
if (renderOpts.events && renderOpts.events.change) {
renderOpts.events.change(row, column.field, rowIndex)
}
}}
onClear={() => {
// 触发清空事件
if (renderOpts.events && renderOpts.events.clear) {
renderOpts.events.clear(row, column.field, rowIndex)
}
}}
/>,
row.defaultSelect === "指定"&&row.fieldType!=='Date' && (
<el-input
class={row.defaultSelect?'my-cell width-50':'my-cell'}
type="text"
v-model={row[prop]}
placeholder={placeholder || `请输入 ${column.title}`}
/>
),
row.defaultSelect === "指定"&&row.fieldType=='Date' && (
<el-date-picker
class={row.defaultSelect?'my-cell width-50':'my-cell'}
v-model={row[prop]}
placeholder={placeholder || `请输入 ${column.title}`}
format={'YYYY-MM-DD'}
valueFormat={'YYYY-MM-DD'}
/>
)
]
}
else
return <el-input class="my-cell" text="text" v-model={row[prop]} placeholder={placeholder ? placeholder : '请输入 ' + column.title} />
}
},
LowNumber: {
default: (renderOpts, { row, column }, isStop) => {
if (isStop) return (<div> <span>{row[column.field]}</span> {stopIcon} </div>)
@@ -183,6 +252,44 @@ export const useRenderVxeColumn = (useType = 'table') => {
)
}
},
LowSelectMultiple: {
default: (renderOpts, { row, column }, isStop = false) => {
const { dicData } = renderOpts
const value = row[column.field]
const valStr=value?dicData.filter(item=>value.includes(item.value)).map(item=>item.label).join(''):''
return <span>{valStr}</span>
},
edit: (renderOpts, { row, rowIndex, column }) => {
const { multiple, filterable, allowCreate, typeKey ,dicData} = renderOpts
interface DictItem {
label: string;
value: string | number;
[key: string]: any; // 兼容其他可能的字段
}
return (
<el-select
popper-class="vxe-table--ignore-clear"
v-model={row[column.field]}
placeholder={'请选择 ' + column.title}
multiple={multiple}
filterable={filterable}
collapseTags={true}
collapseTagsTooltip={true}
allowCreate={allowCreate}
clearable={true}
>
{dicData.map((item: DictItem, index: number) => (
<el-option
key={index} // 建议用 item.value 作为 key更稳定
label={item.label}
value={item.value}
/>
))}
</el-select>
)
}
},
LowSummaryBottomSql: {
default: (renderOpts, { row, column }, isStop = false) => {
const { dicObj } = renderOpts
@@ -266,6 +373,32 @@ export const useRenderVxeColumn = (useType = 'table') => {
</el-popover>
)
}
},
LowButton: {
default: (renderOpts, { row, column }) => {
const { buttonText, disabled } = renderOpts
const isDisabled = typeof disabled === 'function' ? disabled(row) : disabled
if (isDisabled) return <span style="color: #c0c4cc;">-</span>
return <span style="color: #409eff; cursor: pointer;">{buttonText || '操作'}</span>
},
edit: (renderOpts, { row, column }) => {
const { buttonText, buttonType, buttonSize, disabled } = renderOpts
const isDisabled = typeof disabled === 'function' ? disabled(row) : disabled
return (
<el-button
type={buttonType || 'primary'}
size={buttonSize || 'small'}
disabled={isDisabled}
onClick={() => {
if (renderOpts.events && renderOpts.events.click) {
renderOpts.events.click(row)
}
}}
>
{buttonText || '操作'}
</el-button>
)
}
}
}
for (const key in lowControl) {

View File

@@ -40,6 +40,65 @@
</template>
</template>
</DesignPopup>
<!-- 固定列行配置弹窗 -->
<DesignPopup
v-model="fixedColumnDialog.visible"
title="配置固定列内容"
width="600px"
:is-footer="true"
:handleClose="handleFixedColumnClose"
>
<template #default>
<div style="padding: 20px;">
<el-alert
title="提示"
type="info"
:closable="false"
style="margin-bottom: 20px;"
>
为该固定列的每一行配置不同的显示内容点击"添加行"按钮增加新行输入内容后保存
</el-alert>
<div style="margin-bottom: 15px;">
<ElButton type="primary" @click="addFixedColumnRow" size="small">
<Icon icon="ep:plus" />
添加行
</ElButton>
</div>
<div v-if="fixedColumnDialog.rows.length === 0" style="text-align: center; padding: 40px; color: #909399;">
暂无配置请点击"添加行"按钮添加内容
</div>
<div v-else style="max-height: 400px; overflow-y: auto;">
<div
v-for="(row, index) in fixedColumnDialog.rows"
:key="index"
style="display: flex; align-items: center; margin-bottom: 10px;"
>
<span style="width: 80px; color: #606266;"> {{ index + 1 }} </span>
<ElInput
v-model="row.value"
placeholder="请输入该行显示的内容"
style="flex: 1; margin-right: 10px;"
/>
<ElButton
type="danger"
size="small"
@click="removeFixedColumnRow(index)"
:icon="Delete"
>
删除
</ElButton>
</div>
</div>
</div>
</template>
<template #footer>
<ElButton @click="handleFixedColumnCancel"> </ElButton>
<ElButton type="primary" @click="handleFixedColumnSave"> </ElButton>
</template>
</DesignPopup>
</template>
<script setup lang="ts">
@@ -49,6 +108,7 @@ import { tableInfoOption, dicObj } from '../designData'
import { formattingLengStr } from '@/utils/lowDesign'
import { cloneDeep } from 'lodash-es'
import * as DictDataApi from '@/api/system/dict/dict.type'
import { Delete } from '@element-plus/icons-vue'
defineOptions({ name: 'TableInfo' })
const message = useMessage()
@@ -86,15 +146,29 @@ const tabsRef = ref()
// 父字段引用
const parentFieldMap = ref(new Map())
// 固定列行配置弹窗
const fixedColumnDialog = ref({
visible: false,
currentRow: null as any,
rows: [] as Array<{ value: string }>,
originalValue: '' // 保存原始值,用于取消时恢复
})
const fieldList = computed(() => {
let dicData: Array<{ label: string; value: string; type: string }> = []
infoData.value.basics.forEach((item) => {
if (item.fieldCode && item.isDb == 'Y')
dicData.push({
label: `${item.fieldCode}${item.fieldName ? '' + item.fieldName + '' : ''}`,
value: item.fieldCode,
type: item.fieldType
})
if(item.searchDefaultValue&&['当日','本月','本年'].includes(item.searchDefaultValue)){
item.defaultSelect=item.searchDefaultValue
}else if(item.searchDefaultValue){
item.defaultSelect='指定'
}
})
return dicData
})
@@ -223,6 +297,61 @@ const addSubFieldRow = (parentField, parentRowIndex) => {
})
}
// 打开固定列行配置弹窗
const openFixedColumnDialog = (row) => {
fixedColumnDialog.value.currentRow = row
// 保存原始值
fixedColumnDialog.value.originalValue = row.fixedColumnValue || ''
// 将字符串转换为数组:按逗号分割
let rowsArray = []
if (row.fixedColumnValue && typeof row.fixedColumnValue === 'string' && row.fixedColumnValue.trim() !== '') {
rowsArray = row.fixedColumnValue.split(',').map(value => ({ value: value }))
}
fixedColumnDialog.value.rows = rowsArray
fixedColumnDialog.value.visible = true
}
// 添加固定列行
const addFixedColumnRow = () => {
fixedColumnDialog.value.rows.push({ value: '' })
}
// 删除固定列行
const removeFixedColumnRow = (index) => {
fixedColumnDialog.value.rows.splice(index, 1)
}
// 取消固定列配置
const handleFixedColumnCancel = () => {
// 恢复原始值
if (fixedColumnDialog.value.currentRow) {
fixedColumnDialog.value.currentRow.fixedColumnValue = fixedColumnDialog.value.originalValue
}
fixedColumnDialog.value.visible = false
}
// 处理弹窗关闭X按钮或遮罩层
const handleFixedColumnClose = (done: () => void) => {
// 恢复原始值
if (fixedColumnDialog.value.currentRow) {
fixedColumnDialog.value.currentRow.fixedColumnValue = fixedColumnDialog.value.originalValue
}
done()
}
// 保存固定列配置
const handleFixedColumnSave = () => {
if (fixedColumnDialog.value.currentRow) {
// 将数组转换为字符串:使用逗号拼接
const rowValues = fixedColumnDialog.value.rows.map(row => row.value)
fixedColumnDialog.value.currentRow.fixedColumnValue = rowValues.join(',')
message.success(`已保存固定列"${fixedColumnDialog.value.currentRow.fieldName}"的行配置(共${fixedColumnDialog.value.rows.length}行)`)
}
fixedColumnDialog.value.visible = false
}
const initEditInfoData = () => {
const data = tableInfoOption.formattingInitData(props.editInfoData)
const fieldList: any[] = []
@@ -257,7 +386,9 @@ const initEditInfoData = () => {
if (fieldItem.hasChildren === 'Y') {
fieldItem.hasChildren = 'Y'
}
if (!!fieldItem.isHideDimension&&Object.prototype.toString.call(fieldItem.isHideDimension) == '[object String]') {
fieldItem.isHideDimension=fieldItem.isHideDimension.split(',')
}
fieldList.push(fieldItem)
})
@@ -282,12 +413,14 @@ onMounted(() => {
})
}
}
tableInfoOption.infoColumn.fieldColumn.isHideDimension.editRender.dicData = infoData.value.basics.map(({fieldCode,fieldName})=>{
return {label:fieldName,value:fieldCode}
})
// 设置父字段下拉选项
const updateParentFieldOptions = () => {
// 获取所有parentFieldCode为空的字段作为可选父字段
// 获取所有parentFieldCode为空且非固定列的字段作为可选父字段
const parentFieldOptions = infoData.value.basics
.filter(field => !field.parentFieldCode && field.fieldCode && field.fieldName)
.filter(field => !field.parentFieldCode && field.fieldCode && field.fieldName && field.isFixedColumn !== 'Y')
.map(field => ({
label: `${field.fieldName}`,
value: field.fieldCode
@@ -332,11 +465,12 @@ onMounted(() => {
row.isSubField = false;
_lastSelectedValue = '';
}
} else {
// 清空选择
handleParentFieldClear(row);
_lastSelectedValue = '';
}
// else {
// // 清空选择
// handleParentFieldClear(row);
// _lastSelectedValue = '';
//}
// 更新父字段选项
updateParentFieldOptions();
},
@@ -461,6 +595,41 @@ onMounted(() => {
// 初始化父字段选项
updateParentFieldOptions()
// 添加固定列选项的change事件处理
tableInfoOption.infoColumn.fieldColumn.isFixedColumn.editRender.events = {
change: (row) => {
if (row.isFixedColumn === 'Y') {
// 固定列不能有父字段
if (row.parentFieldCode) {
handleParentFieldClear(row)
}
// 固定列不能有子字段
if (row.hasChildren === 'Y') {
row.hasChildren = 'N'
// 清除所有子字段的父子关系
infoData.value.basics.forEach(field => {
if (field.parentFieldCode === row.fieldCode) {
handleParentFieldClear(field)
}
})
}
} else {
// 取消固定列时清空固定列内容
row.fixedColumnValue = ''
}
updateParentFieldOptions()
}
}
// 添加固定列内容配置按钮的点击事件
tableInfoOption.infoColumn.fieldColumn.fixedColumnValue.editRender.events = {
click: (row) => {
if (row.isFixedColumn === 'Y') {
openFixedColumnDialog(row)
}
}
}
// 设置字典Code的下拉选项
DictDataApi.getSimpleDictTypeList().then((dicData) => {
const dicObj = {}

View File

@@ -149,19 +149,29 @@ const infoColumn = {
fieldColumn: {
fieldCode: { title: '字段编码', minWidth: 120, editRender: { name: 'LowInput', verifyEdit: true } },
fieldName: { title: '字段名称', minWidth: 120, editRender: { name: 'LowInput' } },
parentFieldName: { title: '父字段名称', width: 120, editRender: { name: 'LowSelect', dicData: [], filterable: true, clearable: true } },
parentFieldName: { title: '父字段名称', width: 120, editRender: { name: 'LowSelect', dicData: [], filterable: true,allowCreate:true, clearable: true } },
labelI18n: { title: '国际化配置', width: 140, editRender: { name: 'LowMonacoEditorInput', events: {} } },
fieldType: { title: '字段类型', minWidth: 100, editRender: { name: 'LowSelect', verifyEdit: true, dicData: dicObj.fieldType, dicObj: getDicObj('fieldType') } },
width: { title: '列宽', width: 120, align: "center", editRender: { name: 'LowInput', verifyEdit: true } },
isFixedColumn: { title: '固定列', width: 75, align: "center", editRender: { name: 'LowCheckbox' } },
fixedColumnValue: { title: '固定列内容', width: 110, align: "center", editRender: { name: 'LowButton', disabled: (row) => row.isFixedColumn !== 'Y', buttonText: '配置内容', buttonType: 'primary', buttonSize: 'small', events: {} } },
queryIsDb: { title: '接口查询', width: 75, align: "center", editRender: { name: 'LowCheckbox' } },
queryIsWeb: { title: '查询控件', width: 75, align: "center", editRender: { name: 'LowCheckbox' } },
searchDefaultValue: { title: '默认值', width: 180, align: "center", editRender: { name: 'LowInputDefualt' , verifyEdit: true} },
queryMode: { title: '查询模式', width: 130, editRender: { name: 'LowSelect', verifyEdit: true, dicData: dicObj.queryMode, dicObj: getDicObj('queryMode') } },
dictCode: { title: '字典Code', width: 180, editRender: { name: 'LowSelect', verifyEdit: true, filterable: true, noStop: true, dicData: [] } },
isExport: { title: '是否可导出', width: 90, align: "center", editRender: { name: 'LowCheckbox' } },
isHideCol: { title: '隐藏列', width: 90, align: "center", editRender: { name: 'LowCheckbox' } },
// isHideSearch: { title: '隐藏搜索', width: 90, align: "center", editRender: { name: 'LowCheckbox' } },
isAmount: { title: '是否合计', width: 75, align: "center", editRender: { name: 'LowCheckboxSum' } },
isDimension: { title: '是否维度', width: 75, align: "center", editRender: { name: 'LowCheckbox' } },
isHideDimension: { title: '维度隐藏列', width: 180, editRender: { name: 'LowSelectMultiple', verifyEdit: true, filterable: true, multiple:true,dicData: [] } },
isShowSort: { title: '是否排序', width: 75, align: "center", editRender: { name: 'LowCheckbox' } },
hasChildren: { title: '子字段', width: 90, align: "center", editRender: { name: 'LowButton', disabled: (row) => row.isSubField === true, buttonText: '添加子字段', buttonType: 'primary', buttonSize: 'small' } },
//isDynamicGroup: { title: '动态分组', width: 75, align: "center", editRender: { name: 'LowCheckbox' } },
//hasChildren: { title: '子字段', width: 90, align: "center", editRender: { name: 'LowButton', disabled: (row) => row.isSubField === true, buttonText: '添加子字段', buttonType: 'primary', buttonSize: 'small' } },
},
}
@@ -179,6 +189,10 @@ for (const key in infoColumn) {
if (!keys.includes('hasChildren')) {
keys.push('hasChildren')
}
// 确保固定列内容字段被包含
if (!keys.includes('fixedColumnValue')) {
keys.push('fixedColumnValue')
}
}
infoApiKey[apiKey[key]] = keys
}
@@ -187,7 +201,11 @@ for (const key in infoColumn) {
//默认值
const infoDefaultData = {
basics: {
fieldCode: '', fieldName: '', parentFieldName: '', labelI18n: '', fieldType: 'String', queryIsDb: 'N', queryIsWeb: 'N', queryMode: 'LIKE', dictCode: '', isExport: 'Y', isShowSort: 'N', isAmount: '', isDimension: '', hasChildren: 'N', isSubField: false, parentFieldId: '', parentFieldCode: '',
// fieldCode: '', fieldName: '', parentFieldName: '', labelI18n: '', fieldType: 'String', queryIsDb: 'N', queryIsWeb: 'N', queryMode: 'LIKE', dictCode: '',isHideCol:'N', isExport: 'Y', isShowSort: 'N', isAmount: '', isDimension: '',isHideDimension:'', hasChildren: 'N', isSubField: false, parentFieldId: '', parentFieldCode: '',
fieldCode: '', fieldName: '', parentFieldName: '', labelI18n: '', fieldType: 'String', isFixedColumn: 'N', fixedColumnValue: '', queryIsDb: 'N', queryIsWeb: 'N', queryMode: 'LIKE', dictCode: '', isExport: 'Y', isShowSort: 'N', isAmount: '', isDimension: '', isDynamicGroup: 'N', hasChildren: 'N', isSubField: false, parentFieldId: '', parentFieldCode: '',
},
}

View File

@@ -349,12 +349,11 @@ const tableFormVerify = (type) => {
const index = Number(i)
const item = filedData[index]
item.sortNum = index + 1
item.isHideDimension?item.isHideDimension=item.isHideDimension.join(','):''
let messageText = ''
let tabKey = 'mysql'
// 子字段不能再包含子字段
if (item.isSubField && item.hasChildren === 'Y') {
debugger
messageText = `<div style="line-height:24px">
<div>子字段不能包含子字段</div>
<div>序号:${index + 1}</div>

View File

@@ -14,7 +14,7 @@
:row-class-name="rowClassName"
@checkbox-all="(obj) => emit('selection-change', obj)"
@checkbox-change="(obj) => emit('selection-change', obj)"
@cell-click="(obj) => emit('cell-click', obj)"
@cell-click="cellClick"
>
<vxe-column v-if="tabItem.edit" type="checkbox" width="50" align="center"></vxe-column>
<vxe-column type="seq" width="50" align="center"></vxe-column>
@@ -106,7 +106,16 @@ const testClick = (row) => {
console.log('测试按钮被点击:', row)
alert('测试按钮工作正常!')
}
const cellClick = (obj) => {
const scrollLeft = vxeTableRef.value.getScroll().scrollLeft
const scrollTop = vxeTableRef.value.getScroll().scrollTop
emit('cell-click', obj)
nextTick(() => {
setTimeout(() => {
vxeTableRef.value.scrollTo(scrollLeft,scrollTop)
}, 1)
})
}
defineExpose({ vxeTableRef })
</script>

View File

@@ -18,4 +18,26 @@ pnpm run dev
打包:
pnpm run build:prod
pnpm run build:prod
如果报错npm启动Taro框架报错
Error: The specified module could not be found.
\\?\C:\Users\admin\Desktop\app\node_modules\@tarojs\plugin-doctor-win32-x64-msvc\taro-doctor.win32-x64-msvc.node
at Object.Module._extensions..node (node:internal/modules/cjs/loader:1651:18)
at Module.load (node:internal/modules/cjs/loader:1275:32)
at Function.Module._load (node:internal/modules/cjs/loader:1096:12)
at Module.require (node:internal/modules/cjs/loader:1298:19)
at require (node:internal/modules/helpers:182:18)
at Object.<anonymous> (C:\Users\admin\Desktop\app\node_modules\@tarojs\plugin-doctor\js-binding.js:72:29)
at Module._compile (node:internal/modules/cjs/loader:1529:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1613:10)
at Object.newLoader [as .js] (C:\Users\admin\Desktop\app\node_modules\pirates\lib\index.js:134:7)
at Module.load (node:internal/modules/cjs/loader:1275:32) {
code: 'ERR_DLOPEN_FAILED'
}
需安装插件
直接在Microsoft Visual C++ Redistributable latest supported downloads下载最新的VC++插件并安装,问题直接解决。
https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#latest-supported-redistributable-version