diff --git a/src/api/flow/index.js b/src/api/flow/index.js
index 38accec..0695d50 100644
--- a/src/api/flow/index.js
+++ b/src/api/flow/index.js
@@ -263,3 +263,60 @@ export function getOvertimeHoliday(params,isLoading=false) {
isLoading
})
}
+
+// 获取预算年度选项
+export function getBudgetYearOptions(isLoading=false) {
+ return request({
+ method: 'get',
+ url: '/api/budget/budget-data-year-options',
+ isLoading
+ })
+}
+
+// 获取预算数据树
+export function getBudgetDataTree(params, isLoading=false) {
+ return request({
+ method: 'get',
+ url: '/api/budget/budget-data',
+ params,
+ isLoading
+ })
+}
+
+// 获取流程详情用于合同预填
+export function getFlowDetailForContract(flowId, isLoading = false) {
+ return request({
+ method: 'get',
+ url: `/api/oa/flow/detail-for-contract/${flowId}`,
+ isLoading
+ })
+}
+
+// 获取合同设置
+export function getContractSettings(flowId = null, isLoading = false) {
+ return request({
+ method: 'get',
+ url: '/api/oa/flow/contract-settings',
+ params: flowId ? { flow_id: flowId } : {},
+ isLoading
+ })
+}
+
+// 根据flow_id获取合同详情
+export function getContractByFlowId(flowId, isLoading = false) {
+ return request({
+ method: 'get',
+ url: `/api/oa/flow/contract-by-flow/${flowId}`,
+ isLoading
+ })
+}
+
+// 创建合同
+export function createContract(data, isLoading = false) {
+ return request({
+ method: 'post',
+ url: '/api/oa/flow/create-contract',
+ data,
+ isLoading
+ })
+}
diff --git a/src/components/BudgetSourcePickerField.vue b/src/components/BudgetSourcePickerField.vue
new file mode 100644
index 0000000..8fb3195
--- /dev/null
+++ b/src/components/BudgetSourcePickerField.vue
@@ -0,0 +1,297 @@
+
+
+
+
+
+
+
+ {{ displayText }}
+
+
+ ID: {{ normalizedValue }}
+
+
+
选取
+
清除
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+
+ 确定
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/ContractSignField.vue b/src/components/ContractSignField.vue
new file mode 100644
index 0000000..ffd6a9f
--- /dev/null
+++ b/src/components/ContractSignField.vue
@@ -0,0 +1,470 @@
+
+
+
+
+
+
+
+ {{ displayText }}
+
+
+ ID: {{ normalizedValue }}
+
+
+
合同签订
+
重新签订
+
清除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 付款计划
+
+ 新增计划
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 移除
+
+
+
+
+
+
+ 取消
+ 保存
+
+
+
+
+
+
+
+
+
diff --git a/src/utils/formBuilder.js b/src/utils/formBuilder.js
index 7499266..29b2e64 100644
--- a/src/utils/formBuilder.js
+++ b/src/utils/formBuilder.js
@@ -7,6 +7,8 @@ import axios from "axios";
import { flowList } from "@/api/flow";
import MobilePicker from '@/components/MobilePicker/index.vue';
import MobileMultipleSelect from "@/components/MobileMultipleSelect/index.vue";
+import BudgetSourcePickerField from "@/components/BudgetSourcePickerField.vue";
+import ContractSignField from "@/components/ContractSignField.vue";
import { Message } from 'element-ui'
function isJSON(str) {
if (typeof str !== 'string') return false;
@@ -231,6 +233,45 @@ export default function formBuilder(
)
);
break;
+ case "budget-source":
+ // 可写:hidden input + “选取”按钮 + 弹窗(在独立组件内实现)
+ formItem = h(BudgetSourcePickerField, {
+ props: {
+ fieldName: info.name,
+ value: target[info.name],
+ display: target[`${info.name}_display`],
+ },
+ on: {
+ input: (val) => {
+ this.$set(target, info.name, val);
+ },
+ "update:display": (txt) => {
+ // 用于回显(只读/展示)
+ this.$set(target, `${info.name}_display`, txt);
+ },
+ },
+ });
+ break;
+ case "contract-sign":
+ // 合同签订字段:hidden input + "合同签订"按钮 + 弹窗(在独立组件内实现)
+ formItem = h(ContractSignField, {
+ props: {
+ fieldName: info.name,
+ value: target[info.name],
+ display: target[`${info.name}_display`],
+ flowId: this.$route?.query?.flow_id || "",
+ },
+ on: {
+ input: (val) => {
+ this.$set(target, info.name, val);
+ },
+ "update:display": (txt) => {
+ // 用于回显(只读/展示)
+ this.$set(target, `${info.name}_display`, txt);
+ },
+ },
+ });
+ break;
case "file":
formItem = row
? h("vxe-upload", {
@@ -1068,6 +1109,28 @@ export default function formBuilder(
]
);
break;
+ case "budget-source":
+ // 只读模式下显示后端返回的 _display 值
+ const displayFieldName = info.name + '_display';
+ const displayValue = target[displayFieldName] || target[info.name] || '';
+ console.log('[budget-source] 只读模式渲染', {
+ fieldName: info.name,
+ displayFieldName,
+ displayValue,
+ originalValue: target[info.name],
+ targetKeys: Object.keys(target).filter(k => k.includes(info.name)),
+ target: target
+ });
+ formItem = h(
+ "span",
+ {
+ style: {
+ color: "#333",
+ },
+ },
+ displayValue
+ );
+ break;
default:
formItem = h(
"span",
@@ -2056,6 +2119,25 @@ export default function formBuilder(
})
);
break;
+ case "budget-source":
+ // 只读模式下显示后端返回的 _display 值
+ const displayFieldNameMobile = info.name + '_display';
+ const displayValueMobile = target[displayFieldNameMobile] || target[info.name] || '';
+ console.log('[budget-source] 只读模式渲染(移动端)', {
+ fieldName: info.name,
+ displayFieldName: displayFieldNameMobile,
+ displayValue: displayValueMobile,
+ originalValue: target[info.name],
+ targetKeys: Object.keys(target).filter(k => k.includes(info.name)),
+ target: target
+ });
+ formItem = h("van-field", {
+ props: {
+ value: displayValueMobile,
+ readonly: true,
+ },
+ });
+ break;
default:
formItem = h("van-field", {
props: {
diff --git a/src/views/MeetingMinutes/components/AddMeetingMinutes.vue b/src/views/MeetingMinutes/components/AddMeetingMinutes.vue
index 4dcbc0c..a45cfb2 100644
--- a/src/views/MeetingMinutes/components/AddMeetingMinutes.vue
+++ b/src/views/MeetingMinutes/components/AddMeetingMinutes.vue
@@ -99,7 +99,7 @@
style="width: 100%;"
:disabled="type === 'view'"
>
-
+
diff --git a/src/views/flow/create.vue b/src/views/flow/create.vue
index 10b2cb5..3d200ff 100644
--- a/src/views/flow/create.vue
+++ b/src/views/flow/create.vue
@@ -720,7 +720,8 @@ export default {
this.form[key] = jsonObj;
}
} catch (err) {
- if (this.form.hasOwnProperty(key)) {
+ // 允许所有字段被赋值,包括 _display 字段
+ if (this.form.hasOwnProperty(key) || key.endsWith('_display')) {
if (data[key] instanceof Array) {
if (data[key].length > 0 && data[key][0].hasOwnProperty('url')) {
this.form[key] = data[key].map(i => ({
@@ -731,8 +732,9 @@ export default {
} else {
this.form[key] = ''
}
+ } else {
+ this.form[key] = data[key];
}
- this.form[key] = data[key];
}
}
}
diff --git a/src/views/flow/detailCommon.vue b/src/views/flow/detailCommon.vue
index b563608..3f94bef 100644
--- a/src/views/flow/detailCommon.vue
+++ b/src/views/flow/detailCommon.vue
@@ -521,6 +521,10 @@ export default {
} else {
if (/\/detail/.test(this.$route.path) && this.$route.query.flow_id) {
object[field.name] = "";
+ // 为 budget-source 类型字段初始化 _display 字段
+ if (field.type === 'budget-source') {
+ object[field.name + '_display'] = "";
+ }
} else {
if (this.writeableFields.indexOf(field.id) !== -1 || this.readableFields.indexOf(field.id) !== -1) {
object[field.name] = (this.writeableFields.indexOf(field.id) !== -1 && field.default_value) ? field.default_value : (field.type === 'file' ? [] : "");
@@ -599,7 +603,8 @@ export default {
this.form[key] = jsonObj;
}
} catch (err) {
- if (this.form.hasOwnProperty(key)) {
+ // 允许所有字段被赋值,包括 _display 字段
+ if (this.form.hasOwnProperty(key) || key.endsWith('_display')) {
if (data[key] instanceof Array) {
if (data[key].length > 0 && data[key][0].hasOwnProperty('url')) {
this.form[key] = data[key].map(i => ({
@@ -610,8 +615,9 @@ export default {
} else {
this.form[key] = ''
}
+ } else {
+ this.form[key] = data[key];
}
- this.form[key] = data[key];
}
}
}
@@ -707,7 +713,8 @@ export default {
this.form[key] = jsonObj;
}
} catch (err) {
- if (this.form.hasOwnProperty(key)) {
+ // 允许所有字段被赋值,包括 _display 字段
+ if (this.form.hasOwnProperty(key) || key.endsWith('_display')) {
if (data[key] instanceof Array) {
if (data[key].length > 0) {
this.form[key] = data[key];