diff --git a/src/router/index.js b/src/router/index.js
index ad769e9d..9d4358a3 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -26,7 +26,6 @@ const globalRoutes = [
{ path: '/screen-cable-cop-progress', component: _import('modules/longchuang/screen-cable-cop-progress'), name: 'screen-cable-cop-progress', meta: { title: '线缆/COP生产进度看板' } },
{ path: '/screen-renovation-progress', component: _import('modules/longchuang/screen-renovation-progress'), name: 'screen-renovation-progress', meta: { title: '改造项目生产进度看板' } },
{ path: '/screen-factory-overview', component: _import('modules/longchuang/screen-factory-overview'), name: 'screen-factory-overview', meta: { title: '工厂综合运营看板' } },
- { path: '/production-work-report', component: _import('modules/longchuang/production-work-report'), name: 'production-work-report', meta: { title: '生产报工页面' } },
]
// 主入口路由(需嵌套上左右整体布局)
diff --git a/src/views/common/home.vue b/src/views/common/home.vue
index 0dcf4296..fad84601 100644
--- a/src/views/common/home.vue
+++ b/src/views/common/home.vue
@@ -1,10 +1,10 @@
@@ -15,6 +15,7 @@
class="scheme-card"
v-for="row in schemeList"
:key="row.schemeKey"
+ :style="buildSchemeCardStyle(row)"
@click="gotoSchemeResult(row)">
{{ row.count === null ? '-' : row.count }}
{{ row.itemDesc || '未命名方案' }}
@@ -24,6 +25,13 @@
{{ line }}
@@ -50,6 +58,16 @@
+
+
+
+
+
读取菜单搜索字段
保存
@@ -60,8 +78,7 @@
ref="editFieldTable"
:data="editFieldList"
border
- stripe
- class="board-table"
+ stripe class="board-table"
height="50vh"
v-loading="editLoading"
@selection-change="handleEditSelectionChange">
@@ -79,6 +96,25 @@
+
@@ -98,7 +134,7 @@ import {
import { resolveMenuSearchFieldsByMeta } from '../modules/sift/menuSearchFieldResolver'
export default {
- name: 'home',
+ name: 'advanced-search-center',
data () {
return {
schemeLoading: false,
@@ -117,8 +153,10 @@ export default {
menuId: '',
itemNo: null,
itemDesc: '',
- caseInsensitive: true
+ caseInsensitive: true,
+ schemeColor: '#409EFF'
},
+ schemeColorPredefine: ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#9C27B0', '#00ACC1', '#FF7043'],
symbolOptions: [
{ value: 'eq', label: '等于' },
{ value: 'gt', label: '大于' },
@@ -140,12 +178,18 @@ export default {
in: 'in',
ne: '!=',
like: 'like'
- }
+ },
+ defaultSchemeColor: '#409EFF'
}
},
computed: {
menuOptions () {
- return this.menuFlatList.filter(item => item.url && !item.hasChildren && !/^https?:\/\//i.test(item.url))
+ return this.menuFlatList.filter(item =>
+ item.url && // 1. 必须有 url 属性
+ !item.hasChildren && // 2. 必须没有子菜单
+ !/^https?:\/\//i.test(item.url) && // 3. 必须不是 http/https 开头的外部链接
+ item.url.includes('/') // 4. [新增] url 必须包含 '/' 字符
+ )
}
},
activated () {
@@ -211,6 +255,90 @@ export default {
return fieldName + ' ' + symbol + ' ' + item.formula
})
},
+ getSchemeColorStorageKey () {
+ return 'advancedSearchCenterSchemeColorMap_' + String(this.$store.state.user.id || '')
+ },
+ loadStoredSchemeColorMap () {
+ try {
+ var text = localStorage.getItem(this.getSchemeColorStorageKey())
+ var map = text ? JSON.parse(text) : {}
+ return map && typeof map === 'object' ? map : {}
+ } catch (e) {
+ return {}
+ }
+ },
+ saveStoredSchemeColorMap (map) {
+ try {
+ localStorage.setItem(this.getSchemeColorStorageKey(), JSON.stringify(map || {}))
+ } catch (e) {
+ }
+ },
+ normalizeSchemeColor (color) {
+ if (!color) {
+ return ''
+ }
+ var value = String(color).trim()
+ if (!value) {
+ return ''
+ }
+ if (/^#([0-9a-fA-F]{3}){1,2}$/.test(value)) {
+ return value.toUpperCase()
+ }
+ return ''
+ },
+ resolveSchemeColorFromRow (row) {
+ var source = row || {}
+ var candidates = [
+ source.schemeColor,
+ source.colorHex,
+ source.cardColor,
+ source.themeColor,
+ source.color
+ ]
+ for (var i = 0; i < candidates.length; i++) {
+ var normalized = this.normalizeSchemeColor(candidates[i])
+ if (normalized) {
+ return normalized
+ }
+ }
+ return ''
+ },
+ hexToRgba (hex, alpha) {
+ var value = this.normalizeSchemeColor(hex)
+ if (!value) {
+ return 'rgba(64, 158, 255, ' + alpha + ')'
+ }
+ var color = value.slice(1)
+ if (color.length === 3) {
+ color = color.split('').map(char => char + char).join('')
+ }
+ var r = parseInt(color.slice(0, 2), 16)
+ var g = parseInt(color.slice(2, 4), 16)
+ var b = parseInt(color.slice(4, 6), 16)
+ return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')'
+ },
+ buildSchemeCardStyle (row) {
+ var color = this.normalizeSchemeColor((row || {}).schemeColor) || this.defaultSchemeColor
+ return {
+ '--scheme-accent': color,
+ '--scheme-bg-start': this.hexToRgba(color, 0.16),
+ '--scheme-bg-end': this.hexToRgba(color, 0.04),
+ '--scheme-border': this.hexToRgba(color, 0.28)
+ }
+ },
+ upsertStoredSchemeColor (schemeKey, color) {
+ if (!schemeKey) {
+ return
+ }
+ var normalizedColor = this.normalizeSchemeColor(color)
+ var map = this.loadStoredSchemeColorMap()
+ if (normalizedColor) {
+ map[schemeKey] = normalizedColor
+ } else {
+ delete map[schemeKey]
+ }
+ this.saveStoredSchemeColorMap(map)
+ },
toSnakeCaseFieldName (name) {
if (!name) {
return ''
@@ -236,6 +364,7 @@ export default {
async loadSavedSchemeList () {
this.schemeLoading = true
try {
+ var storedColorMap = this.loadStoredSchemeColorMap()
var userId = String(this.$store.state.user.id)
var summaryResp = await searchSavedSchemeSummary({ userId: userId })
var summaryData = summaryResp && summaryResp.data ? summaryResp.data : {}
@@ -281,11 +410,13 @@ export default {
this.schemeList = summaryRows.map(item => {
var key = this.buildSchemeKey(item)
var menu = this.menuFlatList.find(m => String(m.menuId) === String(item.menuId))
+ var rowColor = this.resolveSchemeColorFromRow(item) || this.normalizeSchemeColor(storedColorMap[key]) || this.defaultSchemeColor
return Object.assign({}, item, {
schemeKey: key,
count: countMap[key] !== undefined ? countMap[key] : null,
menuName: item.menuName || (menu ? menu.name : ('菜单' + item.menuId)),
- conditionPreviewList: this.buildConditionPreviewList(detailMap[key])
+ conditionPreviewList: this.buildConditionPreviewList(detailMap[key]),
+ schemeColor: rowColor
})
})
} catch (e) {
@@ -343,6 +474,7 @@ export default {
this.editForm.itemNo = row.itemNo
this.editForm.itemDesc = row.itemDesc
this.editForm.caseInsensitive = true
+ this.editForm.schemeColor = this.normalizeSchemeColor(row.schemeColor) || this.defaultSchemeColor
this.loadEditFields()
},
newScheme () {
@@ -350,7 +482,8 @@ export default {
menuId: '',
itemNo: null,
itemDesc: '',
- caseInsensitive: true
+ caseInsensitive: true,
+ schemeColor: this.defaultSchemeColor
}
this.editFieldList = []
this.editSelectedFieldNameSet = {}
@@ -479,12 +612,17 @@ export default {
this.editSaving = true
try {
var userId = String(this.$store.state.user.id)
+ var normalizedSchemeColor = this.normalizeSchemeColor(this.editForm.schemeColor) || this.defaultSchemeColor
+ var currentItemNo = this.editForm.itemNo
+ var currentMenuId = this.editForm.menuId
+ var currentItemDesc = this.editForm.itemDesc.trim()
var payload = {
menuId: this.editForm.menuId,
userId: userId,
dtsName: 'ADOQResult',
itemNo: this.editForm.itemNo,
itemDesc: this.editForm.itemDesc.trim(),
+ schemeColor: normalizedSchemeColor,
caseSensitiveFlag: this.editForm.caseInsensitive ? 'N' : 'Y',
querySavedDetailList: selectedList.map(item => {
var normalizedItem = this.normalizeOriginalFieldForSql(item)
@@ -508,6 +646,34 @@ export default {
if (data && data.code === 0) {
this.$message.success(data.msg || '保存成功')
await this.loadSavedSchemeList()
+ if (currentItemNo) {
+ var currentKey = this.buildSchemeKey({
+ menuId: currentMenuId,
+ itemNo: currentItemNo
+ })
+ this.upsertStoredSchemeColor(currentKey, normalizedSchemeColor)
+ this.schemeList = this.schemeList.map(item => {
+ if (item.schemeKey === currentKey) {
+ return Object.assign({}, item, { schemeColor: normalizedSchemeColor })
+ }
+ return item
+ })
+ } else {
+ var createdRows = this.schemeList.filter(item =>
+ String(item.menuId) === String(currentMenuId) &&
+ String((item.itemDesc || '')).trim() === currentItemDesc)
+ createdRows.forEach(item => {
+ this.upsertStoredSchemeColor(item.schemeKey, normalizedSchemeColor)
+ })
+ if (createdRows.length) {
+ var createdMap = {}
+ createdRows.forEach(item => { createdMap[item.schemeKey] = true })
+ this.schemeList = this.schemeList.map(item =>
+ createdMap[item.schemeKey]
+ ? Object.assign({}, item, { schemeColor: normalizedSchemeColor })
+ : item)
+ }
+ }
if (!this.editForm.itemNo) {
this.newScheme()
}
@@ -572,12 +738,13 @@ export default {
.mod-advanced-search-center {
padding: 12px;
}
-
.board-table .cell {
line-height: 20px;
height: 20px;
}
-
+.board-table {
+ margin-top: 12px;
+}
.page-header {
display: flex;
align-items: center;
@@ -600,8 +767,8 @@ export default {
.scheme-card {
position: relative;
border-radius: 10px;
- border: 1px solid #ebeef5;
- background: linear-gradient(160deg, #ffffff, #f7f9fc);
+ border: 1px solid var(--scheme-border, #ebeef5);
+ background: linear-gradient(160deg, var(--scheme-bg-start, #ffffff), var(--scheme-bg-end, #f7f9fc));
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
padding: 12px;
cursor: pointer;
@@ -620,7 +787,7 @@ export default {
min-width: 28px;
padding: 2px 8px;
border-radius: 12px;
- background: #1abc9c;
+ background: var(--scheme-accent, #1abc9c);
color: #fff;
font-size: 12px;
text-align: center;
@@ -671,4 +838,3 @@ export default {
margin-top: 8px;
}
-
diff --git a/src/views/modules/sift/advancedSearchCenter.vue b/src/views/modules/sift/advancedSearchCenter.vue
index 06effe95..70798b30 100644
--- a/src/views/modules/sift/advancedSearchCenter.vue
+++ b/src/views/modules/sift/advancedSearchCenter.vue
@@ -15,6 +15,7 @@
class="scheme-card"
v-for="row in schemeList"
:key="row.schemeKey"
+ :style="buildSchemeCardStyle(row)"
@click="gotoSchemeResult(row)">
{{ row.count === null ? '-' : row.count }}
{{ row.itemDesc || '未命名方案' }}
@@ -57,6 +58,13 @@
+
+
+
+
@@ -145,8 +153,10 @@ export default {
menuId: '',
itemNo: null,
itemDesc: '',
- caseInsensitive: true
+ caseInsensitive: true,
+ schemeColor: '#409EFF'
},
+ schemeColorPredefine: ['#409EFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399', '#9C27B0', '#00ACC1', '#FF7043'],
symbolOptions: [
{ value: 'eq', label: '等于' },
{ value: 'gt', label: '大于' },
@@ -168,12 +178,18 @@ export default {
in: 'in',
ne: '!=',
like: 'like'
- }
+ },
+ defaultSchemeColor: '#409EFF'
}
},
computed: {
menuOptions () {
- return this.menuFlatList.filter(item => item.url && !item.hasChildren && !/^https?:\/\//i.test(item.url))
+ return this.menuFlatList.filter(item =>
+ item.url && // 1. 必须有 url 属性
+ !item.hasChildren && // 2. 必须没有子菜单
+ !/^https?:\/\//i.test(item.url) && // 3. 必须不是 http/https 开头的外部链接
+ item.url.includes('/') // 4. [新增] url 必须包含 '/' 字符
+ )
}
},
activated () {
@@ -239,6 +255,90 @@ export default {
return fieldName + ' ' + symbol + ' ' + item.formula
})
},
+ getSchemeColorStorageKey () {
+ return 'advancedSearchCenterSchemeColorMap_' + String(this.$store.state.user.id || '')
+ },
+ loadStoredSchemeColorMap () {
+ try {
+ var text = localStorage.getItem(this.getSchemeColorStorageKey())
+ var map = text ? JSON.parse(text) : {}
+ return map && typeof map === 'object' ? map : {}
+ } catch (e) {
+ return {}
+ }
+ },
+ saveStoredSchemeColorMap (map) {
+ try {
+ localStorage.setItem(this.getSchemeColorStorageKey(), JSON.stringify(map || {}))
+ } catch (e) {
+ }
+ },
+ normalizeSchemeColor (color) {
+ if (!color) {
+ return ''
+ }
+ var value = String(color).trim()
+ if (!value) {
+ return ''
+ }
+ if (/^#([0-9a-fA-F]{3}){1,2}$/.test(value)) {
+ return value.toUpperCase()
+ }
+ return ''
+ },
+ resolveSchemeColorFromRow (row) {
+ var source = row || {}
+ var candidates = [
+ source.schemeColor,
+ source.colorHex,
+ source.cardColor,
+ source.themeColor,
+ source.color
+ ]
+ for (var i = 0; i < candidates.length; i++) {
+ var normalized = this.normalizeSchemeColor(candidates[i])
+ if (normalized) {
+ return normalized
+ }
+ }
+ return ''
+ },
+ hexToRgba (hex, alpha) {
+ var value = this.normalizeSchemeColor(hex)
+ if (!value) {
+ return 'rgba(64, 158, 255, ' + alpha + ')'
+ }
+ var color = value.slice(1)
+ if (color.length === 3) {
+ color = color.split('').map(char => char + char).join('')
+ }
+ var r = parseInt(color.slice(0, 2), 16)
+ var g = parseInt(color.slice(2, 4), 16)
+ var b = parseInt(color.slice(4, 6), 16)
+ return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + alpha + ')'
+ },
+ buildSchemeCardStyle (row) {
+ var color = this.normalizeSchemeColor((row || {}).schemeColor) || this.defaultSchemeColor
+ return {
+ '--scheme-accent': color,
+ '--scheme-bg-start': this.hexToRgba(color, 0.16),
+ '--scheme-bg-end': this.hexToRgba(color, 0.04),
+ '--scheme-border': this.hexToRgba(color, 0.28)
+ }
+ },
+ upsertStoredSchemeColor (schemeKey, color) {
+ if (!schemeKey) {
+ return
+ }
+ var normalizedColor = this.normalizeSchemeColor(color)
+ var map = this.loadStoredSchemeColorMap()
+ if (normalizedColor) {
+ map[schemeKey] = normalizedColor
+ } else {
+ delete map[schemeKey]
+ }
+ this.saveStoredSchemeColorMap(map)
+ },
toSnakeCaseFieldName (name) {
if (!name) {
return ''
@@ -264,6 +364,7 @@ export default {
async loadSavedSchemeList () {
this.schemeLoading = true
try {
+ var storedColorMap = this.loadStoredSchemeColorMap()
var userId = String(this.$store.state.user.id)
var summaryResp = await searchSavedSchemeSummary({ userId: userId })
var summaryData = summaryResp && summaryResp.data ? summaryResp.data : {}
@@ -309,11 +410,13 @@ export default {
this.schemeList = summaryRows.map(item => {
var key = this.buildSchemeKey(item)
var menu = this.menuFlatList.find(m => String(m.menuId) === String(item.menuId))
+ var rowColor = this.resolveSchemeColorFromRow(item) || this.normalizeSchemeColor(storedColorMap[key]) || this.defaultSchemeColor
return Object.assign({}, item, {
schemeKey: key,
count: countMap[key] !== undefined ? countMap[key] : null,
menuName: item.menuName || (menu ? menu.name : ('菜单' + item.menuId)),
- conditionPreviewList: this.buildConditionPreviewList(detailMap[key])
+ conditionPreviewList: this.buildConditionPreviewList(detailMap[key]),
+ schemeColor: rowColor
})
})
} catch (e) {
@@ -371,6 +474,7 @@ export default {
this.editForm.itemNo = row.itemNo
this.editForm.itemDesc = row.itemDesc
this.editForm.caseInsensitive = true
+ this.editForm.schemeColor = this.normalizeSchemeColor(row.schemeColor) || this.defaultSchemeColor
this.loadEditFields()
},
newScheme () {
@@ -378,7 +482,8 @@ export default {
menuId: '',
itemNo: null,
itemDesc: '',
- caseInsensitive: true
+ caseInsensitive: true,
+ schemeColor: this.defaultSchemeColor
}
this.editFieldList = []
this.editSelectedFieldNameSet = {}
@@ -507,12 +612,17 @@ export default {
this.editSaving = true
try {
var userId = String(this.$store.state.user.id)
+ var normalizedSchemeColor = this.normalizeSchemeColor(this.editForm.schemeColor) || this.defaultSchemeColor
+ var currentItemNo = this.editForm.itemNo
+ var currentMenuId = this.editForm.menuId
+ var currentItemDesc = this.editForm.itemDesc.trim()
var payload = {
menuId: this.editForm.menuId,
userId: userId,
dtsName: 'ADOQResult',
itemNo: this.editForm.itemNo,
itemDesc: this.editForm.itemDesc.trim(),
+ schemeColor: normalizedSchemeColor,
caseSensitiveFlag: this.editForm.caseInsensitive ? 'N' : 'Y',
querySavedDetailList: selectedList.map(item => {
var normalizedItem = this.normalizeOriginalFieldForSql(item)
@@ -536,6 +646,34 @@ export default {
if (data && data.code === 0) {
this.$message.success(data.msg || '保存成功')
await this.loadSavedSchemeList()
+ if (currentItemNo) {
+ var currentKey = this.buildSchemeKey({
+ menuId: currentMenuId,
+ itemNo: currentItemNo
+ })
+ this.upsertStoredSchemeColor(currentKey, normalizedSchemeColor)
+ this.schemeList = this.schemeList.map(item => {
+ if (item.schemeKey === currentKey) {
+ return Object.assign({}, item, { schemeColor: normalizedSchemeColor })
+ }
+ return item
+ })
+ } else {
+ var createdRows = this.schemeList.filter(item =>
+ String(item.menuId) === String(currentMenuId) &&
+ String((item.itemDesc || '')).trim() === currentItemDesc)
+ createdRows.forEach(item => {
+ this.upsertStoredSchemeColor(item.schemeKey, normalizedSchemeColor)
+ })
+ if (createdRows.length) {
+ var createdMap = {}
+ createdRows.forEach(item => { createdMap[item.schemeKey] = true })
+ this.schemeList = this.schemeList.map(item =>
+ createdMap[item.schemeKey]
+ ? Object.assign({}, item, { schemeColor: normalizedSchemeColor })
+ : item)
+ }
+ }
if (!this.editForm.itemNo) {
this.newScheme()
}
@@ -626,8 +764,8 @@ export default {
.scheme-card {
position: relative;
border-radius: 10px;
- border: 1px solid #ebeef5;
- background: linear-gradient(160deg, #ffffff, #f7f9fc);
+ border: 1px solid var(--scheme-border, #ebeef5);
+ background: linear-gradient(160deg, var(--scheme-bg-start, #ffffff), var(--scheme-bg-end, #f7f9fc));
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.06);
padding: 12px;
cursor: pointer;
@@ -646,7 +784,7 @@ export default {
min-width: 28px;
padding: 2px 8px;
border-radius: 12px;
- background: #1abc9c;
+ background: var(--scheme-accent, #1abc9c);
color: #fff;
font-size: 12px;
text-align: center;
diff --git a/src/views/modules/sift/menuSearchFieldResolver.js b/src/views/modules/sift/menuSearchFieldResolver.js
index ec372bc1..d347c917 100644
--- a/src/views/modules/sift/menuSearchFieldResolver.js
+++ b/src/views/modules/sift/menuSearchFieldResolver.js
@@ -17,8 +17,16 @@ function extractTemplateContent(vueSource) {
return ''
}
var text = typeof vueSource === 'string' ? vueSource : (vueSource.default || '')
- var match = text.match(/([\s\S]*?)<\/template>/)
- return match && match[1] ? match[1] : ''
+ var startMatch = text.match(/]*)?>/i)
+ if (!startMatch || typeof startMatch.index !== 'number') {
+ return ''
+ }
+ var start = startMatch.index + startMatch[0].length
+ var end = text.lastIndexOf('')
+ if (end <= start) {
+ return ''
+ }
+ return text.substring(start, end)
}
function normalizeLabel(rawLabel, fallbackName) {
@@ -48,12 +56,12 @@ function parseSearchFieldsFromVueSource(vueSource) {
var blocks = template.match(formItemRegex) || []
var orderSeq = 1
blocks.forEach(block => {
- var modelMatch = block.match(/v-model\s*=\s*"searchData\.([A-Za-z0-9_]+)"/)
+ var modelMatch = block.match(/v-model\s*=\s*["']searchData\.([A-Za-z0-9_]+)["']/)
if (!modelMatch || !modelMatch[1]) {
return
}
var fieldName = modelMatch[1]
- var labelMatch = block.match(/(?:\s:label|\slabel)\s*=\s*"([^"]*)"/)
+ var labelMatch = block.match(/(?:\s:label|\slabel)\s*=\s*["']([^"']*)["']/)
var fieldCaption = normalizeLabel(labelMatch ? labelMatch[1] : '', fieldName)
var dataType = /