weizong song 4 months ago
parent b6f2278fcb
commit 7f2e8e9c56

@ -113,3 +113,7 @@ echo ""

@ -35,3 +35,11 @@ export function permissions () {
}
})
}
export function getModuleRoles(module) {
return request({
url: `/api/auth/module-roles/${module}`,
method: "get",
isLoading: false
})
}

@ -31,6 +31,46 @@ export function getPaymentCategoryTemplateElements(categoryId, isLoading = false
})
}
/**
* 根据合同ID获取支付列表
* 后端路由GET /api/budget/payments?contract_id=xxx
*/
export function getPaymentsByContractId(contractId, params = {}, isLoading = false) {
return request({
method: 'get',
url: '/api/budget/payments',
params: {
contract_id: contractId,
...params
},
isLoading
})
}
/**
* 获取 Budget 合同详情
* 后端路由GET /api/budget/contracts/{id}
*/
export function getBudgetContractDetail(id, isLoading = false) {
return request({
method: 'get',
url: `/api/budget/contracts/${id}`,
isLoading
})
}
/**
* 获取模板元素定义用于 checklist/附件等最原始 options
* 后端路由GET /api/budget/template-elements/{id}
*/
export function getTemplateElementDetail(id, isLoading = false) {
return request({
method: 'get',
url: `/api/budget/template-elements/${id}`,
isLoading
})
}
/**
* 获取明细表格字段定义
* 后端路由GET /api/budget/detail-table-fields/{elementId}

File diff suppressed because it is too large Load Diff

@ -1386,7 +1386,21 @@ export default function formBuilder(
case "budget-source":
// 只读模式下显示后端返回的 _display 值
const displayFieldName = info.name + '_display';
const displayValue = target[displayFieldName] || target[info.name] || '';
let displayValue = target[displayFieldName] || '';
// 如果 _display 为空尝试从原始值生成显示文本避免显示JSON
if (!displayValue && target[info.name]) {
const rawValue = target[info.name];
// 如果是字符串且看起来像JSON不直接显示
if (typeof rawValue === 'string' && (rawValue.trim().startsWith('{') || rawValue.trim().startsWith('['))) {
displayValue = ''; // 不显示原始JSON
} else if (typeof rawValue === 'object' || Array.isArray(rawValue)) {
displayValue = ''; // 不显示对象或数组
} else {
displayValue = String(rawValue);
}
}
console.log('[budget-source] 只读模式渲染', {
fieldName: info.name,
displayFieldName,
@ -2591,7 +2605,21 @@ export default function formBuilder(
case "budget-source":
// 只读模式下显示后端返回的 _display 值
const displayFieldNameMobile = info.name + '_display';
const displayValueMobile = target[displayFieldNameMobile] || target[info.name] || '';
let displayValueMobile = target[displayFieldNameMobile] || '';
// 如果 _display 为空尝试从原始值生成显示文本避免显示JSON
if (!displayValueMobile && target[info.name]) {
const rawValue = target[info.name];
// 如果是字符串且看起来像JSON不直接显示
if (typeof rawValue === 'string' && (rawValue.trim().startsWith('{') || rawValue.trim().startsWith('['))) {
displayValueMobile = ''; // 不显示原始JSON
} else if (typeof rawValue === 'object' || Array.isArray(rawValue)) {
displayValueMobile = ''; // 不显示对象或数组
} else {
displayValueMobile = String(rawValue);
}
}
console.log('[budget-source] 只读模式渲染(移动端)', {
fieldName: info.name,
displayFieldName: displayFieldNameMobile,

@ -4,6 +4,7 @@
<vxe-toolbar export print ref="toolbar">
<template #buttons>
<el-button
v-if="hasMeetingMinutesPermission"
icon="el-icon-plus"
type="primary"
size="small"
@ -121,10 +122,15 @@
<el-button size="small" type="primary" @click="viewRowEvent(row)"
>查看</el-button
>
<el-button size="small" type="warning" @click="editRowEvent(row)"
<el-button
v-if="hasMeetingMinutesPermission"
size="small"
type="warning"
@click="editRowEvent(row)"
>编辑</el-button
>
<el-button
v-if="hasMeetingMinutesPermission"
size="small"
type="danger"
@click="destroyRowEvent(row)"
@ -174,6 +180,7 @@ import axios from "axios";
import { getToken } from "@/utils/auth";
import { uploadSize } from '@/settings';
import { formatFileSize } from '@/utils'
import { getModuleRoles } from "@/api/me";
export default {
components: {
@ -185,6 +192,7 @@ export default {
isShowAdd: false,
isShowType: 'add',
loading: false,
moduleRoles: [], // Oa
select: {
page: 1,
page_size: 20,
@ -213,8 +221,13 @@ export default {
}
};
},
// 使Oa
hasMeetingMinutesPermission() {
return this.moduleRoles && this.moduleRoles.includes('会议纪要管理');
},
},
created() {
async created() {
await this.getModuleRoles();
this.getList();
},
mounted() {
@ -222,6 +235,19 @@ export default {
},
methods: {
formatFileSize,
// Oa
async getModuleRoles() {
try {
const moduleName = window.MODULE_NAME || window.location.pathname.replaceAll(/\//g, "") || process.env.VUE_APP_MODULE_NAME || 'oa';
const res = await getModuleRoles(moduleName);
if (res && Array.isArray(res)) {
this.moduleRoles = res;
}
} catch (err) {
console.error('获取模块角色失败:', err);
this.moduleRoles = [];
}
},
previewFile(file) {
//
if (file.url) {

@ -145,12 +145,127 @@ export default {
},
],
flows: {},
tempFlowList: []
tempFlowList: [],
// fill_flow_title watcher form
_unwatchFillFlowTitle: null,
};
},
methods: {
getToken,
request,
_parseMultiValue(raw) {
if (raw === null || raw === undefined) return [];
if (Array.isArray(raw)) return raw.map((v) => String(v).trim()).filter(Boolean);
const s = String(raw).trim();
if (!s) return [];
// filterRequestColumns "|" ","
const parts = s.includes("|") ? s.split("|") : (s.includes(",") ? s.split(",") : [s]);
return parts.map((v) => String(v).trim()).filter(Boolean);
},
_getOptionLabelFromSelectionModelItems(field, value) {
const items = field?.selection_model_items;
if (!Array.isArray(items) || items.length === 0) return null;
const v = String(value);
const hit = items.find((it) => String(it?.id ?? it?.value ?? it?.key ?? "") === v) || null;
if (!hit) return null;
return hit?.name ?? hit?.label ?? hit?.title ?? (hit?.id !== undefined ? String(hit.id) : null);
},
_getOptionLabelFromStub(field, value) {
const stub = field?.stub;
if (!stub) return null;
const lines = String(stub).split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
const v = String(value);
// stub =
const hit = lines.find((line) => line === v) || null;
return hit;
},
_formatSelectFillValue(field, rawValue) {
const values = this._parseMultiValue(rawValue);
if (values.length === 0) return "";
// labellabel - value1value2
if (values.length > 1) {
return `${field.label || field.name} - ${values.join("、")}`;
}
// label - value
const value = values[0];
const optionLabel =
this._getOptionLabelFromSelectionModelItems(field, value) ||
this._getOptionLabelFromStub(field, value) ||
value;
return `${optionLabel} - ${value}`;
},
setupFillFlowTitleWatcher() {
try {
if (typeof this._unwatchFillFlowTitle === "function") {
this._unwatchFillFlowTitle();
}
} catch (e) {
// ignore
}
this._unwatchFillFlowTitle = null;
// flow_id //
if (this.$route?.query?.flow_id) return;
const list = Array.isArray(this.fields) ? this.fields : [];
const allowedTypes = new Set(["text", "textarea", "select"]);
const fillField = list.find((f) => Number(f?.fill_flow_title) === 1 && allowedTypes.has(f?.type) && f?.name);
if (!fillField) return;
const fieldName = fillField.name;
this._unwatchFillFlowTitle = this.$watch(
() => (this.form ? this.form[fieldName] : undefined),
(newVal) => {
// isFirstNode
if (!this.isFirstNode || this.$route?.query?.flow_id) return;
let v = "";
if (fillField.type === "select") {
v = this._formatSelectFillValue(fillField, newVal);
} else {
v = newVal === null || newVal === undefined ? "" : String(newVal).trim();
}
if (!v) return;
this.$set(this.form, "flow_title", v);
},
{ immediate: true }
);
},
//
installSafeEventListenerGuard() {
if (window.__oa_safe_listener_guard_installed) return;
window.__oa_safe_listener_guard_installed = true;
const map = new WeakMap();
const origAdd = EventTarget && EventTarget.prototype && EventTarget.prototype.addEventListener;
const origRemove = EventTarget && EventTarget.prototype && EventTarget.prototype.removeEventListener;
if (!origAdd || !origRemove) return;
EventTarget.prototype.addEventListener = function (type, listener, options) {
if (typeof listener === "function") {
let wrapped = map.get(listener);
if (!wrapped) {
wrapped = function (...args) {
try {
return listener.apply(this, args);
} catch (e) {
console.error("[OA Custom Script] event listener error:", e);
}
};
map.set(listener, wrapped);
}
return origAdd.call(this, type, wrapped, options);
}
return origAdd.call(this, type, listener, options);
};
EventTarget.prototype.removeEventListener = function (type, listener, options) {
if (typeof listener === "function") {
const wrapped = map.get(listener);
return origRemove.call(this, type, wrapped || listener, options);
}
return origRemove.call(this, type, listener, options);
};
},
async validate() {
this.customValidate()
const $elForm = this.$refs['elForm']
@ -184,6 +299,8 @@ export default {
watch: {
originalForm(newVal) {
this.form = deepCopy(newVal)
// watcher
this.$nextTick(() => this.setupFillFlowTitleWatcher());
// let copyForm = deepCopy(newVal);
// for (let key in copyForm) {
// Object.defineProperty(this.form, key, {
@ -194,12 +311,20 @@ export default {
// })
// }
},
fields: {
handler() {
// fill_flow_title watcher
this.$nextTick(() => this.setupFillFlowTitleWatcher());
},
deep: true
},
scriptContent(newVal) {
if (newVal) {
try {
// 使 $nextTick DOM
this.$nextTick(() => {
try {
this.installSafeEventListenerGuard();
new Function(newVal).bind(this)();
} catch (err) {
console.error('脚本执行错误:', err);

@ -73,6 +73,8 @@ export default {
jointlySignLog: [], // log
form: {},
// fill_flow_title watcher form
_unwatchFillFlowTitle: null,
file: {
ggg: []
},
@ -108,6 +110,115 @@ export default {
}
},
methods: {
setupFillFlowTitleWatcher() {
try {
if (typeof this._unwatchFillFlowTitle === "function") {
this._unwatchFillFlowTitle();
}
} catch (e) {
// ignore
}
this._unwatchFillFlowTitle = null;
// flow_id //
if (this.$route?.query?.flow_id) return;
const list = Array.isArray(this.fields) ? this.fields : [];
const allowedTypes = new Set(["text", "textarea", "select"]);
const fillField = list.find((f) => Number(f?.fill_flow_title) === 1 && allowedTypes.has(f?.type) && f?.name);
if (!fillField) return;
const fieldName = fillField.name;
this._unwatchFillFlowTitle = this.$watch(
() => (this.form ? this.form[fieldName] : undefined),
(newVal) => {
// isFirstNode
if (!this.isFirstNode || this.$route?.query?.flow_id) return;
const parseMultiValue = (raw) => {
if (raw === null || raw === undefined) return [];
if (Array.isArray(raw)) return raw.map((v) => String(v).trim()).filter(Boolean);
const s = String(raw).trim();
if (!s) return [];
const parts = s.includes("|") ? s.split("|") : (s.includes(",") ? s.split(",") : [s]);
return parts.map((v) => String(v).trim()).filter(Boolean);
};
const getOptionLabelFromSelectionModelItems = (field, value) => {
const items = field?.selection_model_items;
if (!Array.isArray(items) || items.length === 0) return null;
const v = String(value);
const hit = items.find((it) => String(it?.id ?? it?.value ?? it?.key ?? "") === v) || null;
if (!hit) return null;
return hit?.name ?? hit?.label ?? hit?.title ?? (hit?.id !== undefined ? String(hit.id) : null);
};
const getOptionLabelFromStub = (field, value) => {
const stub = field?.stub;
if (!stub) return null;
const lines = String(stub).split(/\\r?\\n/).map((s) => s.trim()).filter(Boolean);
const v = String(value);
return lines.find((line) => line === v) || null;
};
const formatSelectFillValue = (field, rawValue) => {
const values = parseMultiValue(rawValue);
if (values.length === 0) return "";
if (values.length > 1) {
return `${field.label || field.name} - ${values.join("、")}`;
}
const value = values[0];
const optionLabel =
getOptionLabelFromSelectionModelItems(field, value) ||
getOptionLabelFromStub(field, value) ||
value;
return `${optionLabel} - ${value}`;
};
let v = "";
if (fillField.type === "select") {
v = formatSelectFillValue(fillField, newVal);
} else {
v = newVal === null || newVal === undefined ? "" : String(newVal).trim();
}
if (!v) return;
this.$set(this.form, "flow_title", v);
},
{ immediate: true }
);
},
//
installSafeEventListenerGuard() {
if (window.__oa_safe_listener_guard_installed) return;
window.__oa_safe_listener_guard_installed = true;
const map = new WeakMap();
const origAdd = EventTarget && EventTarget.prototype && EventTarget.prototype.addEventListener;
const origRemove = EventTarget && EventTarget.prototype && EventTarget.prototype.removeEventListener;
if (!origAdd || !origRemove) return;
EventTarget.prototype.addEventListener = function (type, listener, options) {
if (typeof listener === "function") {
let wrapped = map.get(listener);
if (!wrapped) {
wrapped = function (...args) {
try {
return listener.apply(this, args);
} catch (e) {
console.error("[OA Custom Script] event listener error:", e);
}
};
map.set(listener, wrapped);
}
return origAdd.call(this, type, wrapped, options);
}
return origAdd.call(this, type, listener, options);
};
EventTarget.prototype.removeEventListener = function (type, listener, options) {
if (typeof listener === "function") {
const wrapped = map.get(listener);
return origRemove.call(this, type, wrapped || listener, options);
}
return origRemove.call(this, type, listener, options);
};
},
request,
getToken,
async validate() {
@ -134,6 +245,13 @@ export default {
},
originalForm(newVal) {
this.form = deepCopy(newVal)
this.$nextTick(() => this.setupFillFlowTitleWatcher());
},
fields: {
handler() {
this.$nextTick(() => this.setupFillFlowTitleWatcher());
},
deep: true
},
scriptContent(newVal) {
if(newVal) {
@ -141,6 +259,7 @@ export default {
// 使 $nextTick DOM
this.$nextTick(() => {
try {
this.installSafeEventListenerGuard();
new Function(newVal).bind(this)();
} catch (err) {
console.error('脚本执行错误:', err);

File diff suppressed because it is too large Load Diff

@ -184,3 +184,7 @@ A: 这是正常的Vue CLI使用内容哈希来生成文件名。只要HTML文

Loading…
Cancel
Save