@yugu-ui/flow-ui 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +273 -216
- package/dist/style.css +64 -63
- package/dist/timeline/FlowNodesPanel.d.ts +32 -0
- package/dist/timeline/timeline-mapping.d.ts +24 -0
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ export { default as FlowConfig } from './config/FlowConfig';
|
|
|
2
2
|
export { default as FlowConfigField } from './config/FlowConfigField';
|
|
3
3
|
export { default as FlowConfigMore } from './config/FlowConfigMore';
|
|
4
4
|
export { default as FlowLayout } from './timeline/FlowLayout';
|
|
5
|
+
export { default as FlowNodesPanel } from './timeline/FlowNodesPanel';
|
|
6
|
+
export type { FlowTimelineData, FlowStatusTag } from './timeline/timeline-mapping';
|
|
5
7
|
export { default as FlowNode } from './timeline/FlowNode';
|
|
6
8
|
export { default as FlowAvatar } from './timeline/FlowAvatar';
|
|
7
9
|
export { default as ApprovalModal } from './timeline/ApprovalModal';
|
package/dist/index.js
CHANGED
|
@@ -5959,8 +5959,254 @@ var ApprovalModal_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/*
|
|
|
5959
5959
|
}
|
|
5960
5960
|
}), [["__scopeId", "data-v-fb3bd9c6"]]);
|
|
5961
5961
|
//#endregion
|
|
5962
|
+
//#region src/base-ui/base-card-item.vue?vue&type=script&setup=true&lang.ts
|
|
5963
|
+
var _hoisted_1$5 = { class: "header" };
|
|
5964
|
+
var _hoisted_2$3 = { class: "header-left" };
|
|
5965
|
+
var _hoisted_3$2 = { class: "title" };
|
|
5966
|
+
var _hoisted_4$2 = {
|
|
5967
|
+
key: 0,
|
|
5968
|
+
class: "subtitle"
|
|
5969
|
+
};
|
|
5970
|
+
var _hoisted_5$1 = { class: "header-right" };
|
|
5971
|
+
//#endregion
|
|
5972
|
+
//#region src/base-ui/base-card-item.vue
|
|
5973
|
+
var base_card_item_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
5974
|
+
name: "BaseCardItem",
|
|
5975
|
+
inheritAttrs: false,
|
|
5976
|
+
__name: "base-card-item",
|
|
5977
|
+
props: {
|
|
5978
|
+
title: { default: "" },
|
|
5979
|
+
subtitle: { default: "" },
|
|
5980
|
+
contentPadding: { default: 16 }
|
|
5981
|
+
},
|
|
5982
|
+
setup(__props) {
|
|
5983
|
+
const props = __props;
|
|
5984
|
+
const slots = useSlots();
|
|
5985
|
+
const contentStyle = computed(() => ({ padding: typeof props.contentPadding === "number" ? `${props.contentPadding}px` : props.contentPadding }));
|
|
5986
|
+
const showHeader = computed(() => {
|
|
5987
|
+
return !!props.title || !!props.subtitle || !!slots["subtitle"] || !!slots["header-extra"];
|
|
5988
|
+
});
|
|
5989
|
+
return (_ctx, _cache) => {
|
|
5990
|
+
return openBlock(), createBlock(unref(NCard), mergeProps({
|
|
5991
|
+
class: "base-card-item base-card-item-spacing",
|
|
5992
|
+
"content-class": "base-card-item-content",
|
|
5993
|
+
"header-class": "base-card-item-header",
|
|
5994
|
+
"content-style": contentStyle.value
|
|
5995
|
+
}, _ctx.$attrs), createSlots({
|
|
5996
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "default", {}, void 0, true)]),
|
|
5997
|
+
_: 2
|
|
5998
|
+
}, [showHeader.value ? {
|
|
5999
|
+
name: "header",
|
|
6000
|
+
fn: withCtx(() => [createElementVNode("div", _hoisted_1$5, [createElementVNode("div", _hoisted_2$3, [createElementVNode("span", _hoisted_3$2, toDisplayString(__props.title), 1), renderSlot(_ctx.$slots, "subtitle", {}, () => [__props.subtitle ? (openBlock(), createElementBlock("span", _hoisted_4$2, toDisplayString(__props.subtitle), 1)) : createCommentVNode("", true)], true)]), createElementVNode("div", _hoisted_5$1, [renderSlot(_ctx.$slots, "header-extra", {}, void 0, true)])])]),
|
|
6001
|
+
key: "0"
|
|
6002
|
+
} : void 0]), 1040, ["content-style"]);
|
|
6003
|
+
};
|
|
6004
|
+
}
|
|
6005
|
+
}), [["__scopeId", "data-v-e9115957"]]);
|
|
6006
|
+
//#endregion
|
|
6007
|
+
//#region src/timeline/timeline-mapping.ts
|
|
6008
|
+
/** 固定尾节点:节点有数据时渲染追加(display 下状态按流程是否走完覆盖),接口返回与提交数据均不包含 */
|
|
6009
|
+
var END_NODE = {
|
|
6010
|
+
type: "end",
|
|
6011
|
+
name: "结束",
|
|
6012
|
+
desc: "流程结束",
|
|
6013
|
+
status: "pending",
|
|
6014
|
+
users: []
|
|
6015
|
+
};
|
|
6016
|
+
/** 人员处理记录映射:一行 records 对应一条展示记录(finished=0 未完成行过滤)——result 优先:result=2 同意;result=3 或 is_reject=1 驳回行按记录操作人判定方向(record.user_id=挂载人员 id 主动驳回「驳回到 rejecters.name」、否则被驳回「被 user_name 驳回」、名字均缺且 flowRejected=true 展示「驳回到 发起人」、否则「驳回」);result=0 不展示 */
|
|
6017
|
+
function toUserRecords(user, flowRejected) {
|
|
6018
|
+
const toFiles = (files) => files?.length ? files.map((file) => ({
|
|
6019
|
+
name: file.original_filename || file.file_name || "附件",
|
|
6020
|
+
url: file.url
|
|
6021
|
+
})) : void 0;
|
|
6022
|
+
const rows = [];
|
|
6023
|
+
(Array.isArray(user.records) ? user.records : []).forEach((record) => {
|
|
6024
|
+
if (record.finished === 0) return;
|
|
6025
|
+
const isSelf = record.user_id != null && String(record.user_id) === String(user.id);
|
|
6026
|
+
const rejectTo = isSelf ? record.rejecters?.name : void 0;
|
|
6027
|
+
const rejectBy = isSelf ? void 0 : record.user_name;
|
|
6028
|
+
const base = {
|
|
6029
|
+
time: record.assertion_time ? formatTime(record.assertion_time, "YYYY-MM-DD HH:mm") : void 0,
|
|
6030
|
+
opinion: record.suggest || void 0,
|
|
6031
|
+
attachments: toFiles(record.attachments)
|
|
6032
|
+
};
|
|
6033
|
+
if (record.result === 2) {
|
|
6034
|
+
rows.push({
|
|
6035
|
+
type: "approve",
|
|
6036
|
+
...base
|
|
6037
|
+
});
|
|
6038
|
+
return;
|
|
6039
|
+
}
|
|
6040
|
+
if (record.is_reject !== 1 && record.result !== 3) return;
|
|
6041
|
+
rows.push({
|
|
6042
|
+
type: "reject",
|
|
6043
|
+
...base,
|
|
6044
|
+
rejectNode: rejectTo || (!rejectTo && !rejectBy && flowRejected ? "发起人" : void 0),
|
|
6045
|
+
rejectBy
|
|
6046
|
+
});
|
|
6047
|
+
});
|
|
6048
|
+
return rows;
|
|
6049
|
+
}
|
|
6050
|
+
/** 已处理记录(result=2/3 或 is_reject=1,与 toUserRecords 口径一致)中处理时间(assertion_time 毫秒时间戳)最新的一条是否为驳回(result=3 或 is_reject=1);空集合返回 false,records 数组顺序不可靠必须按时间数值比较 */
|
|
6051
|
+
function isLatestRowsReject(rows) {
|
|
6052
|
+
if (!rows.length) return false;
|
|
6053
|
+
const latest = rows.reduce((acc, cur) => (cur.assertion_time ?? 0) >= (acc.assertion_time ?? 0) ? cur : acc, rows[0]);
|
|
6054
|
+
return latest.result === 3 || latest.is_reject === 1;
|
|
6055
|
+
}
|
|
6056
|
+
/** display 模式审批时间线映射:节点快照直接映射 FlowNodeItem——发起人节点独立判定:流程被驳回到发起人(finished=1 且 status=-1)标红、其余一律完成绿色;其余节点按实例 finished、节点自带 finished(1=已完成)推导:进行中节点优先取第一个「未完成且 status=1 待处理」的节点,无激活节点时兜底认第一个存在待处理任务行(users.records 中 result=0 且 finished=0)的节点为当前节点,仍无则回退第一个未完成节点,有激活/兜底节点时其后节点一律待开始(剥离上一轮残留),流程被驳回终止时其余节点一律灰色待开始;人员状态按本人最新一条操作记录展示(已处理记录 result=2/3 或 is_reject=1 中 assertion_time 最新一条:驳回标红、同意标绿),无操作记录随节点状态(进行中节点淡黄、其余灰色未处理) */
|
|
6057
|
+
function mapTimelineNodes(data) {
|
|
6058
|
+
const d = data;
|
|
6059
|
+
if (!d?.nodes?.length) return [];
|
|
6060
|
+
const instanceFinished = d.finished === 1;
|
|
6061
|
+
const instanceRejected = instanceFinished && d.status === -1;
|
|
6062
|
+
const activeIndex = d.nodes.findIndex((item) => item.status === 1 && item.finished !== 1);
|
|
6063
|
+
const pendingIndex = !instanceFinished && activeIndex < 0 ? d.nodes.findIndex((item) => item.users.some((user) => (Array.isArray(user.records) ? user.records : []).some((record) => record.result === 0 && record.finished === 0))) : -1;
|
|
6064
|
+
const currentIndex = activeIndex > -1 ? activeIndex : pendingIndex > -1 ? pendingIndex : instanceFinished ? -1 : d.nodes.findIndex((item) => item.finished !== 1);
|
|
6065
|
+
return d.nodes.map((node, index) => {
|
|
6066
|
+
const forcePending = !instanceFinished && (activeIndex > -1 || pendingIndex > -1) && index > currentIndex;
|
|
6067
|
+
const nodeStatus = node.type === "start" ? instanceRejected ? "rejected" : "done" : instanceRejected ? "pending" : forcePending ? "pending" : !instanceFinished && index === currentIndex ? "doing" : instanceFinished || node.finished === 1 || currentIndex > -1 && index < currentIndex ? "done" : "pending";
|
|
6068
|
+
const users = node.users.map((user) => {
|
|
6069
|
+
const records = toUserRecords(user, instanceRejected);
|
|
6070
|
+
const rawRows = Array.isArray(user.records) ? user.records : [];
|
|
6071
|
+
const processedRows = rawRows.filter((record) => record.result === 2 || record.result === 3 || record.is_reject === 1);
|
|
6072
|
+
const status = processedRows.length ? isLatestRowsReject(processedRows) ? "rejected" : "done" : nodeStatus === "doing" ? "doing" : nodeStatus === "done" && node.mode === "orsign" ? "pending" : nodeStatus;
|
|
6073
|
+
return {
|
|
6074
|
+
id: user.id,
|
|
6075
|
+
name: user.name,
|
|
6076
|
+
status,
|
|
6077
|
+
pendingTask: rawRows.some((record) => record.result === 0 && record.finished === 0),
|
|
6078
|
+
...records.length ? { records } : {}
|
|
6079
|
+
};
|
|
6080
|
+
});
|
|
6081
|
+
return {
|
|
6082
|
+
type: node.type,
|
|
6083
|
+
id: node.node_id,
|
|
6084
|
+
status: nodeStatus,
|
|
6085
|
+
name: node.name,
|
|
6086
|
+
desc: node.desc ?? void 0,
|
|
6087
|
+
time: node.created_time && (node.finished === 1 || nodeStatus === "done" || nodeStatus === "rejected") ? formatTime(node.created_time, "YYYY-MM-DD HH:mm") : void 0,
|
|
6088
|
+
mode: node.mode,
|
|
6089
|
+
selfSelect: node.selfSelect,
|
|
6090
|
+
users
|
|
6091
|
+
};
|
|
6092
|
+
});
|
|
6093
|
+
}
|
|
6094
|
+
/** 组装完整节点链:有数据统一追加 END 结束节点,无数据为空数组走空态;display(非 form)流程全部完成时 END 状态同步 done;末节点已是 end(后端快照自带 end、或调用方传入已组装过 END 的节点链,如 FlowLayout → FlowNodesPanel)时幂等跳过,防止结束节点重复渲染 */
|
|
6095
|
+
function withEndNode(baseNodes, mode) {
|
|
6096
|
+
if (!baseNodes.length) return [];
|
|
6097
|
+
if (baseNodes[baseNodes.length - 1].type === "end") return baseNodes;
|
|
6098
|
+
const allDone = mode !== "form" && baseNodes.every((node) => node.status === "done");
|
|
6099
|
+
return [...baseNodes, {
|
|
6100
|
+
...END_NODE,
|
|
6101
|
+
status: allDone ? "done" : "pending"
|
|
6102
|
+
}];
|
|
6103
|
+
}
|
|
6104
|
+
/** 折叠面板标题流程状态标签:仅 display 模式且有详情数据时推导——finished=1 且 status=-1 已拒绝(驳回到发起人终止)、finished=1 已同意、未结束进行中;form 发起模式无实例状态不展示,数据未返回前隐藏避免先展示「进行中」再跳变 */
|
|
6105
|
+
function getFlowStatusTag(mode, data) {
|
|
6106
|
+
if (mode === "form" || !data) return void 0;
|
|
6107
|
+
if (data.finished === 1) return data.status === -1 ? {
|
|
6108
|
+
text: "已拒绝",
|
|
6109
|
+
type: "error"
|
|
6110
|
+
} : {
|
|
6111
|
+
text: "已同意",
|
|
6112
|
+
type: "success"
|
|
6113
|
+
};
|
|
6114
|
+
return {
|
|
6115
|
+
text: "进行中",
|
|
6116
|
+
type: "warning"
|
|
6117
|
+
};
|
|
6118
|
+
}
|
|
6119
|
+
//#endregion
|
|
6120
|
+
//#region src/timeline/FlowNodesPanel.vue?vue&type=script&setup=true&lang.ts
|
|
6121
|
+
var _hoisted_1$4 = { class: "flow-node__title" };
|
|
6122
|
+
var _hoisted_2$2 = { class: "flow-node__list" };
|
|
6123
|
+
//#endregion
|
|
6124
|
+
//#region src/timeline/FlowNodesPanel.vue
|
|
6125
|
+
var FlowNodesPanel_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
6126
|
+
name: "FlowNodesPanel",
|
|
6127
|
+
__name: "FlowNodesPanel",
|
|
6128
|
+
props: /* @__PURE__ */ mergeModels({
|
|
6129
|
+
data: {},
|
|
6130
|
+
nodes: {},
|
|
6131
|
+
mode: {},
|
|
6132
|
+
loading: {
|
|
6133
|
+
type: Boolean,
|
|
6134
|
+
default: false
|
|
6135
|
+
},
|
|
6136
|
+
title: { default: "审批流程" },
|
|
6137
|
+
statusTag: {}
|
|
6138
|
+
}, {
|
|
6139
|
+
"selectedUsersMap": { default: () => ({}) },
|
|
6140
|
+
"selectedUsersMapModifiers": {}
|
|
6141
|
+
}),
|
|
6142
|
+
emits: ["update:selectedUsersMap"],
|
|
6143
|
+
setup(__props) {
|
|
6144
|
+
const props = __props;
|
|
6145
|
+
/** 自选审批人节点已选人员(key=节点名) */
|
|
6146
|
+
const selectedUsersMap = useModel(__props, "selectedUsersMap");
|
|
6147
|
+
/** 面板渲染节点链:有数据时追加 END 结束节点,无数据为空数组走空态 */
|
|
6148
|
+
const panelNodes = computed(() => {
|
|
6149
|
+
if (props.mode === "form") return withEndNode(props.nodes ?? [], "form");
|
|
6150
|
+
return withEndNode(props.data ? mapTimelineNodes(props.data) : props.nodes ?? [], "display");
|
|
6151
|
+
});
|
|
6152
|
+
/** 折叠面板标题流程状态标签 */
|
|
6153
|
+
const panelStatusTag = computed(() => props.data ? getFlowStatusTag(props.mode, props.data) : props.statusTag);
|
|
6154
|
+
return (_ctx, _cache) => {
|
|
6155
|
+
const _component_NTag = NTag;
|
|
6156
|
+
const _component_NEmpty = NEmpty;
|
|
6157
|
+
const _component_NSpin = NSpin;
|
|
6158
|
+
const _component_NCollapseItem = NCollapseItem;
|
|
6159
|
+
const _component_NCollapse = NCollapse;
|
|
6160
|
+
return openBlock(), createBlock(base_card_item_default, { "content-padding": 0 }, {
|
|
6161
|
+
default: withCtx(() => [createVNode(_component_NCollapse, {
|
|
6162
|
+
class: "flow-node",
|
|
6163
|
+
"arrow-placement": "right",
|
|
6164
|
+
"default-expanded-names": ["flow"]
|
|
6165
|
+
}, {
|
|
6166
|
+
default: withCtx(() => [createVNode(_component_NCollapseItem, { name: "flow" }, {
|
|
6167
|
+
header: withCtx(() => [createElementVNode("span", _hoisted_1$4, [createTextVNode(toDisplayString(__props.title) + " ", 1), panelStatusTag.value ? (openBlock(), createBlock(_component_NTag, {
|
|
6168
|
+
key: 0,
|
|
6169
|
+
type: panelStatusTag.value.type,
|
|
6170
|
+
size: "small"
|
|
6171
|
+
}, {
|
|
6172
|
+
default: withCtx(() => [createTextVNode(toDisplayString(panelStatusTag.value.text), 1)]),
|
|
6173
|
+
_: 1
|
|
6174
|
+
}, 8, ["type"])) : createCommentVNode("", true)])]),
|
|
6175
|
+
default: withCtx(() => [createVNode(_component_NSpin, {
|
|
6176
|
+
show: __props.loading,
|
|
6177
|
+
class: "w-full"
|
|
6178
|
+
}, {
|
|
6179
|
+
default: withCtx(() => [createElementVNode("div", _hoisted_2$2, [(openBlock(true), createElementBlock(Fragment, null, renderList(panelNodes.value, (node) => {
|
|
6180
|
+
return openBlock(), createBlock(FlowNode_default, {
|
|
6181
|
+
key: node.id ?? node.name,
|
|
6182
|
+
"selected-users": selectedUsersMap.value[node.name],
|
|
6183
|
+
"onUpdate:selectedUsers": ($event) => selectedUsersMap.value[node.name] = $event,
|
|
6184
|
+
node,
|
|
6185
|
+
mode: __props.mode
|
|
6186
|
+
}, null, 8, [
|
|
6187
|
+
"selected-users",
|
|
6188
|
+
"onUpdate:selectedUsers",
|
|
6189
|
+
"node",
|
|
6190
|
+
"mode"
|
|
6191
|
+
]);
|
|
6192
|
+
}), 128)), !panelNodes.value.length ? (openBlock(), createBlock(_component_NEmpty, {
|
|
6193
|
+
key: 0,
|
|
6194
|
+
description: __props.mode === "form" ? "必填字段填写完整后展示流程节点" : "暂无流程节点"
|
|
6195
|
+
}, null, 8, ["description"])) : createCommentVNode("", true)])]),
|
|
6196
|
+
_: 1
|
|
6197
|
+
}, 8, ["show"])]),
|
|
6198
|
+
_: 1
|
|
6199
|
+
})]),
|
|
6200
|
+
_: 1
|
|
6201
|
+
})]),
|
|
6202
|
+
_: 1
|
|
6203
|
+
});
|
|
6204
|
+
};
|
|
6205
|
+
}
|
|
6206
|
+
}), [["__scopeId", "data-v-25e5146e"]]);
|
|
6207
|
+
//#endregion
|
|
5962
6208
|
//#region src/base-ui/yg-confirm.vue?vue&type=script&setup=true&lang.ts
|
|
5963
|
-
var _hoisted_1$
|
|
6209
|
+
var _hoisted_1$3 = { class: "yg-confirm" };
|
|
5964
6210
|
//#endregion
|
|
5965
6211
|
//#region src/base-ui/yg-confirm.vue
|
|
5966
6212
|
var yg_confirm_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
@@ -6048,7 +6294,7 @@ var yg_confirm_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @_
|
|
|
6048
6294
|
}
|
|
6049
6295
|
});
|
|
6050
6296
|
return (_ctx, _cache) => {
|
|
6051
|
-
return openBlock(), createElementBlock("div", _hoisted_1$
|
|
6297
|
+
return openBlock(), createElementBlock("div", _hoisted_1$3, [__props.triggerType === "button" ? (openBlock(), createBlock(unref(NButton), mergeProps({ key: 0 }, buttonProps.value, { onClick: handleOpen }), createSlots({
|
|
6052
6298
|
default: withCtx(() => [renderSlot(_ctx.$slots, "default", {}, void 0, true)]),
|
|
6053
6299
|
_: 2
|
|
6054
6300
|
}, [_ctx.$slots.icon ? {
|
|
@@ -6092,7 +6338,7 @@ var yg_confirm_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @_
|
|
|
6092
6338
|
}), [["__scopeId", "data-v-a65cb6d5"]]);
|
|
6093
6339
|
//#endregion
|
|
6094
6340
|
//#region src/base-ui/base-card.vue?vue&type=script&setup=true&lang.ts
|
|
6095
|
-
var _hoisted_1$
|
|
6341
|
+
var _hoisted_1$2 = {
|
|
6096
6342
|
key: 0,
|
|
6097
6343
|
class: "base-card-footer"
|
|
6098
6344
|
};
|
|
@@ -6164,7 +6410,7 @@ var base_card_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__
|
|
|
6164
6410
|
}, {
|
|
6165
6411
|
default: withCtx(() => [renderSlot(_ctx.$slots, "default", {}, void 0, true)]),
|
|
6166
6412
|
_: 3
|
|
6167
|
-
}, 8, ["show"]), showFooter.value ? (openBlock(), createElementBlock("div", _hoisted_1$
|
|
6413
|
+
}, 8, ["show"]), showFooter.value ? (openBlock(), createElementBlock("div", _hoisted_1$2, [renderSlot(_ctx.$slots, "footer", {}, () => [createVNode(unref(NSpace), { size: 12 }, {
|
|
6168
6414
|
default: withCtx(() => [
|
|
6169
6415
|
renderSlot(_ctx.$slots, "footer-extra", {}, void 0, true),
|
|
6170
6416
|
__props.cancelConfig?.show && __props.cancelConfig?.doubleConfirm ? (openBlock(), createBlock(yg_confirm_default, {
|
|
@@ -6221,51 +6467,6 @@ var base_card_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__
|
|
|
6221
6467
|
}
|
|
6222
6468
|
}), [["__scopeId", "data-v-956107d8"]]);
|
|
6223
6469
|
//#endregion
|
|
6224
|
-
//#region src/base-ui/base-card-item.vue?vue&type=script&setup=true&lang.ts
|
|
6225
|
-
var _hoisted_1$3 = { class: "header" };
|
|
6226
|
-
var _hoisted_2$3 = { class: "header-left" };
|
|
6227
|
-
var _hoisted_3$2 = { class: "title" };
|
|
6228
|
-
var _hoisted_4$2 = {
|
|
6229
|
-
key: 0,
|
|
6230
|
-
class: "subtitle"
|
|
6231
|
-
};
|
|
6232
|
-
var _hoisted_5$1 = { class: "header-right" };
|
|
6233
|
-
//#endregion
|
|
6234
|
-
//#region src/base-ui/base-card-item.vue
|
|
6235
|
-
var base_card_item_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
6236
|
-
name: "BaseCardItem",
|
|
6237
|
-
inheritAttrs: false,
|
|
6238
|
-
__name: "base-card-item",
|
|
6239
|
-
props: {
|
|
6240
|
-
title: { default: "" },
|
|
6241
|
-
subtitle: { default: "" },
|
|
6242
|
-
contentPadding: { default: 16 }
|
|
6243
|
-
},
|
|
6244
|
-
setup(__props) {
|
|
6245
|
-
const props = __props;
|
|
6246
|
-
const slots = useSlots();
|
|
6247
|
-
const contentStyle = computed(() => ({ padding: typeof props.contentPadding === "number" ? `${props.contentPadding}px` : props.contentPadding }));
|
|
6248
|
-
const showHeader = computed(() => {
|
|
6249
|
-
return !!props.title || !!props.subtitle || !!slots["subtitle"] || !!slots["header-extra"];
|
|
6250
|
-
});
|
|
6251
|
-
return (_ctx, _cache) => {
|
|
6252
|
-
return openBlock(), createBlock(unref(NCard), mergeProps({
|
|
6253
|
-
class: "base-card-item base-card-item-spacing",
|
|
6254
|
-
"content-class": "base-card-item-content",
|
|
6255
|
-
"header-class": "base-card-item-header",
|
|
6256
|
-
"content-style": contentStyle.value
|
|
6257
|
-
}, _ctx.$attrs), createSlots({
|
|
6258
|
-
default: withCtx(() => [renderSlot(_ctx.$slots, "default", {}, void 0, true)]),
|
|
6259
|
-
_: 2
|
|
6260
|
-
}, [showHeader.value ? {
|
|
6261
|
-
name: "header",
|
|
6262
|
-
fn: withCtx(() => [createElementVNode("div", _hoisted_1$3, [createElementVNode("div", _hoisted_2$3, [createElementVNode("span", _hoisted_3$2, toDisplayString(__props.title), 1), renderSlot(_ctx.$slots, "subtitle", {}, () => [__props.subtitle ? (openBlock(), createElementBlock("span", _hoisted_4$2, toDisplayString(__props.subtitle), 1)) : createCommentVNode("", true)], true)]), createElementVNode("div", _hoisted_5$1, [renderSlot(_ctx.$slots, "header-extra", {}, void 0, true)])])]),
|
|
6263
|
-
key: "0"
|
|
6264
|
-
} : void 0]), 1040, ["content-style"]);
|
|
6265
|
-
};
|
|
6266
|
-
}
|
|
6267
|
-
}), [["__scopeId", "data-v-e9115957"]]);
|
|
6268
|
-
//#endregion
|
|
6269
6470
|
//#region src/adapter/wflow.ts
|
|
6270
6471
|
/** 获取流程配置详情(GET /wflow/config/{config_id}) */
|
|
6271
6472
|
function fetchGetWflowConfigDetail(configId) {
|
|
@@ -6307,12 +6508,8 @@ function fetchWflowUndoReject(data) {
|
|
|
6307
6508
|
});
|
|
6308
6509
|
}
|
|
6309
6510
|
//#endregion
|
|
6310
|
-
//#region src/timeline/FlowLayout.vue?vue&type=script&setup=true&lang.ts
|
|
6311
|
-
var _hoisted_1$2 = { class: "flow-node__title" };
|
|
6312
|
-
var _hoisted_2$2 = { class: "flow-node__list" };
|
|
6313
|
-
//#endregion
|
|
6314
6511
|
//#region src/timeline/FlowLayout.vue
|
|
6315
|
-
var FlowLayout_default = /* @__PURE__ */
|
|
6512
|
+
var FlowLayout_default = /* @__PURE__ */ defineComponent({
|
|
6316
6513
|
name: "FlowLayout",
|
|
6317
6514
|
__name: "FlowLayout",
|
|
6318
6515
|
props: {
|
|
@@ -6338,20 +6535,7 @@ var FlowLayout_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @_
|
|
|
6338
6535
|
/** 是否展示驳回操作:仅 display 模式且 undo_finished=0(未处理);form 发起模式无驳回 */
|
|
6339
6536
|
const canReject = computed(() => props.mode !== "form" && props.timelineData?.undo_finished === 0);
|
|
6340
6537
|
/** 折叠面板标题流程状态标签:仅 display 模式展示,颜色映射 已同意=success / 已拒绝=error / 进行中=warning */
|
|
6341
|
-
const flowStatusTag = computed(() =>
|
|
6342
|
-
if (props.mode === "form" || !props.timelineData) return void 0;
|
|
6343
|
-
if (props.timelineData.finished === 1) return props.timelineData.status === -1 ? {
|
|
6344
|
-
text: "已拒绝",
|
|
6345
|
-
type: "error"
|
|
6346
|
-
} : {
|
|
6347
|
-
text: "已同意",
|
|
6348
|
-
type: "success"
|
|
6349
|
-
};
|
|
6350
|
-
return {
|
|
6351
|
-
text: "进行中",
|
|
6352
|
-
type: "warning"
|
|
6353
|
-
};
|
|
6354
|
-
});
|
|
6538
|
+
const flowStatusTag = computed(() => getFlowStatusTag(props.mode, props.timelineData));
|
|
6355
6539
|
/** 关闭:通知消费页(display=关闭详情页,form=取消发起);页签关闭逻辑由消费页承担 */
|
|
6356
6540
|
function handleClose() {
|
|
6357
6541
|
emit("close");
|
|
@@ -6452,14 +6636,6 @@ var FlowLayout_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @_
|
|
|
6452
6636
|
}
|
|
6453
6637
|
emit("success");
|
|
6454
6638
|
}
|
|
6455
|
-
/** 固定尾节点:form/display 模式节点有数据时渲染追加(display 下状态按流程是否走完覆盖),接口返回与提交数据均不包含 */
|
|
6456
|
-
const END_NODE = {
|
|
6457
|
-
type: "end",
|
|
6458
|
-
name: "结束",
|
|
6459
|
-
desc: "流程结束",
|
|
6460
|
-
status: "pending",
|
|
6461
|
-
users: []
|
|
6462
|
-
};
|
|
6463
6639
|
/** form 模式节点链:条件字段全部填写后经 fetchFlowNodes 请求所得(不同条件值得到不同节点),为空时节点区展示空态提示;不含 end 结束节点 */
|
|
6464
6640
|
const formNodes = ref([]);
|
|
6465
6641
|
/** form 模式节点请求中:节点区 NSpin loading(仅节点接口请求期间为 true,display 模式恒为 false) */
|
|
@@ -6509,100 +6685,11 @@ var FlowLayout_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @_
|
|
|
6509
6685
|
onMounted(() => {
|
|
6510
6686
|
flowConfigReady = initFlowConfig();
|
|
6511
6687
|
});
|
|
6512
|
-
/**
|
|
6513
|
-
|
|
6514
|
-
const toFiles = (files) => files?.length ? files.map((file) => ({
|
|
6515
|
-
name: file.original_filename || file.file_name || "附件",
|
|
6516
|
-
url: file.url
|
|
6517
|
-
})) : void 0;
|
|
6518
|
-
const rows = [];
|
|
6519
|
-
(Array.isArray(user.records) ? user.records : []).forEach((record) => {
|
|
6520
|
-
if (record.finished === 0) return;
|
|
6521
|
-
const isSelf = record.user_id != null && String(record.user_id) === String(user.id);
|
|
6522
|
-
const rejectTo = isSelf ? record.rejecters?.name : void 0;
|
|
6523
|
-
const rejectBy = isSelf ? void 0 : record.user_name;
|
|
6524
|
-
const base = {
|
|
6525
|
-
time: record.assertion_time ? formatTime(record.assertion_time, "YYYY-MM-DD HH:mm") : void 0,
|
|
6526
|
-
opinion: record.suggest || void 0,
|
|
6527
|
-
attachments: toFiles(record.attachments)
|
|
6528
|
-
};
|
|
6529
|
-
if (record.result === 2) {
|
|
6530
|
-
rows.push({
|
|
6531
|
-
type: "approve",
|
|
6532
|
-
...base
|
|
6533
|
-
});
|
|
6534
|
-
return;
|
|
6535
|
-
}
|
|
6536
|
-
if (record.is_reject !== 1 && record.result !== 3) return;
|
|
6537
|
-
rows.push({
|
|
6538
|
-
type: "reject",
|
|
6539
|
-
...base,
|
|
6540
|
-
rejectNode: rejectTo || (!rejectTo && !rejectBy && flowRejected ? "发起人" : void 0),
|
|
6541
|
-
rejectBy
|
|
6542
|
-
});
|
|
6543
|
-
});
|
|
6544
|
-
return rows;
|
|
6545
|
-
}
|
|
6546
|
-
/** 已处理记录(result=2/3 或 is_reject=1,与 toUserRecords 口径一致)中处理时间(assertion_time 毫秒时间戳)最新的一条是否为驳回(result=3 或 is_reject=1);空集合返回 false,records 数组顺序不可靠必须按时间数值比较 */
|
|
6547
|
-
function isLatestRowsReject(rows) {
|
|
6548
|
-
if (!rows.length) return false;
|
|
6549
|
-
const latest = rows.reduce((acc, cur) => (cur.assertion_time ?? 0) >= (acc.assertion_time ?? 0) ? cur : acc, rows[0]);
|
|
6550
|
-
return latest.result === 3 || latest.is_reject === 1;
|
|
6551
|
-
}
|
|
6552
|
-
/** display 模式审批时间线:节点快照直接映射 FlowNodeItem——发起人节点独立判定:流程被驳回到发起人(finished=1 且 status=-1)标红、其余一律完成绿色;其余节点按实例 finished、节点自带 finished(1=已完成)推导:进行中节点优先取第一个「未完成且 status=1 待处理」的节点,无激活节点时兜底认第一个存在待处理任务行(users.records 中 result=0 且 finished=0)的节点为当前节点,仍无则回退第一个未完成节点,有激活/兜底节点时其后节点一律待开始(剥离上一轮残留),流程被驳回终止时其余节点一律灰色待开始;人员状态按本人最新一条操作记录展示(已处理记录 result=2/3 或 is_reject=1 中 assertion_time 最新一条:驳回标红、同意标绿),无操作记录随节点状态(进行中节点淡黄、其余灰色未处理) */
|
|
6553
|
-
const displayNodes = computed(() => {
|
|
6554
|
-
const d = props.timelineData;
|
|
6555
|
-
if (!d?.nodes?.length) return [];
|
|
6556
|
-
const instanceFinished = d.finished === 1;
|
|
6557
|
-
const instanceRejected = instanceFinished && d.status === -1;
|
|
6558
|
-
const activeIndex = d.nodes.findIndex((item) => item.status === 1 && item.finished !== 1);
|
|
6559
|
-
const pendingIndex = !instanceFinished && activeIndex < 0 ? d.nodes.findIndex((item) => item.users.some((user) => (Array.isArray(user.records) ? user.records : []).some((record) => record.result === 0 && record.finished === 0))) : -1;
|
|
6560
|
-
const currentIndex = activeIndex > -1 ? activeIndex : pendingIndex > -1 ? pendingIndex : instanceFinished ? -1 : d.nodes.findIndex((item) => item.finished !== 1);
|
|
6561
|
-
return d.nodes.map((node, index) => {
|
|
6562
|
-
const forcePending = !instanceFinished && (activeIndex > -1 || pendingIndex > -1) && index > currentIndex;
|
|
6563
|
-
const nodeStatus = node.type === "start" ? instanceRejected ? "rejected" : "done" : instanceRejected ? "pending" : forcePending ? "pending" : !instanceFinished && index === currentIndex ? "doing" : instanceFinished || node.finished === 1 || currentIndex > -1 && index < currentIndex ? "done" : "pending";
|
|
6564
|
-
const users = node.users.map((user) => {
|
|
6565
|
-
const records = toUserRecords(user, instanceRejected);
|
|
6566
|
-
const rawRows = Array.isArray(user.records) ? user.records : [];
|
|
6567
|
-
const processedRows = rawRows.filter((record) => record.result === 2 || record.result === 3 || record.is_reject === 1);
|
|
6568
|
-
const status = processedRows.length ? isLatestRowsReject(processedRows) ? "rejected" : "done" : nodeStatus === "doing" ? "doing" : nodeStatus === "done" && node.mode === "orsign" ? "pending" : nodeStatus;
|
|
6569
|
-
return {
|
|
6570
|
-
id: user.id,
|
|
6571
|
-
name: user.name,
|
|
6572
|
-
status,
|
|
6573
|
-
pendingTask: rawRows.some((record) => record.result === 0 && record.finished === 0),
|
|
6574
|
-
...records.length ? { records } : {}
|
|
6575
|
-
};
|
|
6576
|
-
});
|
|
6577
|
-
return {
|
|
6578
|
-
type: node.type,
|
|
6579
|
-
id: node.node_id,
|
|
6580
|
-
status: nodeStatus,
|
|
6581
|
-
name: node.name,
|
|
6582
|
-
desc: node.desc ?? void 0,
|
|
6583
|
-
time: node.created_time && (node.finished === 1 || nodeStatus === "done" || nodeStatus === "rejected") ? formatTime(node.created_time, "YYYY-MM-DD HH:mm") : void 0,
|
|
6584
|
-
mode: node.mode,
|
|
6585
|
-
selfSelect: node.selfSelect,
|
|
6586
|
-
users
|
|
6587
|
-
};
|
|
6588
|
-
});
|
|
6589
|
-
});
|
|
6688
|
+
/** display 模式审批时间线:节点快照直接映射 FlowNodeItem(映射规则详见 timeline-mapping.mapTimelineNodes) */
|
|
6689
|
+
const displayNodes = computed(() => mapTimelineNodes(props.timelineData));
|
|
6590
6690
|
/** 按渲染模式取节点链:form=发起节点链、display=审批时间线(均为组件内部处理所得),有数据时统一追加 END 结束节点,无数据为空数组走空态 */
|
|
6591
|
-
const flowNodes = computed(() =>
|
|
6592
|
-
const baseNodes = props.mode === "form" ? formNodes.value : displayNodes.value;
|
|
6593
|
-
if (!baseNodes.length) return [];
|
|
6594
|
-
const allDone = props.mode !== "form" && baseNodes.every((node) => node.status === "done");
|
|
6595
|
-
return [...baseNodes, {
|
|
6596
|
-
...END_NODE,
|
|
6597
|
-
status: allDone ? "done" : "pending"
|
|
6598
|
-
}];
|
|
6599
|
-
});
|
|
6691
|
+
const flowNodes = computed(() => withEndNode(props.mode === "form" ? formNodes.value : displayNodes.value, props.mode));
|
|
6600
6692
|
return (_ctx, _cache) => {
|
|
6601
|
-
const _component_NTag = NTag;
|
|
6602
|
-
const _component_NEmpty = NEmpty;
|
|
6603
|
-
const _component_NSpin = NSpin;
|
|
6604
|
-
const _component_NCollapseItem = NCollapseItem;
|
|
6605
|
-
const _component_NCollapse = NCollapse;
|
|
6606
6693
|
const _component_NButton = NButton;
|
|
6607
6694
|
return openBlock(), createBlock(base_card_default, {
|
|
6608
6695
|
loading: __props.loading,
|
|
@@ -6619,55 +6706,25 @@ var FlowLayout_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @_
|
|
|
6619
6706
|
onCancel: handleClose
|
|
6620
6707
|
}, createSlots({
|
|
6621
6708
|
default: withCtx(() => [
|
|
6622
|
-
renderSlot(_ctx.$slots, "default"
|
|
6623
|
-
createVNode(
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
}, 8, ["type"])) : createCommentVNode("", true)])]),
|
|
6638
|
-
default: withCtx(() => [createVNode(_component_NSpin, {
|
|
6639
|
-
show: flowNodesLoading.value,
|
|
6640
|
-
class: "w-full"
|
|
6641
|
-
}, {
|
|
6642
|
-
default: withCtx(() => [createElementVNode("div", _hoisted_2$2, [(openBlock(true), createElementBlock(Fragment, null, renderList(flowNodes.value, (node) => {
|
|
6643
|
-
return openBlock(), createBlock(FlowNode_default, {
|
|
6644
|
-
key: node.id ?? node.name,
|
|
6645
|
-
"selected-users": selfSelectedUsersMap.value[node.name],
|
|
6646
|
-
"onUpdate:selectedUsers": ($event) => selfSelectedUsersMap.value[node.name] = $event,
|
|
6647
|
-
node,
|
|
6648
|
-
mode: __props.mode
|
|
6649
|
-
}, null, 8, [
|
|
6650
|
-
"selected-users",
|
|
6651
|
-
"onUpdate:selectedUsers",
|
|
6652
|
-
"node",
|
|
6653
|
-
"mode"
|
|
6654
|
-
]);
|
|
6655
|
-
}), 128)), !flowNodes.value.length ? (openBlock(), createBlock(_component_NEmpty, {
|
|
6656
|
-
key: 0,
|
|
6657
|
-
description: __props.mode === "form" ? "必填字段填写完整后展示流程节点" : "暂无流程节点"
|
|
6658
|
-
}, null, 8, ["description"])) : createCommentVNode("", true)])]),
|
|
6659
|
-
_: 1
|
|
6660
|
-
}, 8, ["show"])]),
|
|
6661
|
-
_: 1
|
|
6662
|
-
})]),
|
|
6663
|
-
_: 1
|
|
6664
|
-
})]),
|
|
6665
|
-
_: 1
|
|
6666
|
-
}),
|
|
6709
|
+
renderSlot(_ctx.$slots, "default"),
|
|
6710
|
+
createVNode(FlowNodesPanel_default, {
|
|
6711
|
+
"selected-users-map": selfSelectedUsersMap.value,
|
|
6712
|
+
"onUpdate:selectedUsersMap": _cache[0] || (_cache[0] = ($event) => selfSelectedUsersMap.value = $event),
|
|
6713
|
+
nodes: flowNodes.value,
|
|
6714
|
+
mode: __props.mode,
|
|
6715
|
+
loading: flowNodesLoading.value,
|
|
6716
|
+
"status-tag": flowStatusTag.value
|
|
6717
|
+
}, null, 8, [
|
|
6718
|
+
"selected-users-map",
|
|
6719
|
+
"nodes",
|
|
6720
|
+
"mode",
|
|
6721
|
+
"loading",
|
|
6722
|
+
"status-tag"
|
|
6723
|
+
]),
|
|
6667
6724
|
canReject.value ? (openBlock(), createBlock(ApprovalModal_default, {
|
|
6668
6725
|
key: 0,
|
|
6669
6726
|
visible: approvalVisible.value,
|
|
6670
|
-
"onUpdate:visible": _cache[
|
|
6727
|
+
"onUpdate:visible": _cache[1] || (_cache[1] = ($event) => approvalVisible.value = $event),
|
|
6671
6728
|
type: approvalType.value,
|
|
6672
6729
|
nodes: flowNodes.value,
|
|
6673
6730
|
onSubmitted: handleApprovalSubmit
|
|
@@ -6691,7 +6748,7 @@ var FlowLayout_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @_
|
|
|
6691
6748
|
} : void 0]), 1032, ["loading", "confirm-config"]);
|
|
6692
6749
|
};
|
|
6693
6750
|
}
|
|
6694
|
-
})
|
|
6751
|
+
});
|
|
6695
6752
|
//#endregion
|
|
6696
6753
|
//#region src/flow-list/modules/FlowListItem.vue?vue&type=script&setup=true&lang.ts
|
|
6697
6754
|
var _hoisted_1$1 = {
|
|
@@ -6910,4 +6967,4 @@ var flow_list_default = /* @__PURE__ */ _plugin_vue_export_helper_default(/* @__
|
|
|
6910
6967
|
}
|
|
6911
6968
|
}), [["__scopeId", "data-v-3484a8df"]]);
|
|
6912
6969
|
//#endregion
|
|
6913
|
-
export { ApprovalModal_default as ApprovalModal, FLOW_UI_KEY, FlowAvatar_default as FlowAvatar, FlowConfig_default as FlowConfig, FlowConfigField_default as FlowConfigField, FlowConfigMore_default as FlowConfigMore, FlowLayout_default as FlowLayout, flow_list_default as FlowList, FlowNode_default as FlowNode, person_selector_default as PersonSelector, createFlowUI, fetchGetWflowConfigDetail, fetchGetWflowFlowNodes, fetchLaunchWflowFlow, fetchWflowUndoApprove, fetchWflowUndoReject, getRemoteOptions, isRemoteField, useFlowUI };
|
|
6970
|
+
export { ApprovalModal_default as ApprovalModal, FLOW_UI_KEY, FlowAvatar_default as FlowAvatar, FlowConfig_default as FlowConfig, FlowConfigField_default as FlowConfigField, FlowConfigMore_default as FlowConfigMore, FlowLayout_default as FlowLayout, flow_list_default as FlowList, FlowNode_default as FlowNode, FlowNodesPanel_default as FlowNodesPanel, person_selector_default as PersonSelector, createFlowUI, fetchGetWflowConfigDetail, fetchGetWflowFlowNodes, fetchLaunchWflowFlow, fetchWflowUndoApprove, fetchWflowUndoReject, getRemoteOptions, isRemoteField, useFlowUI };
|
package/dist/style.css
CHANGED
|
@@ -2120,58 +2120,6 @@ to {
|
|
|
2120
2120
|
width: 100%;
|
|
2121
2121
|
}
|
|
2122
2122
|
|
|
2123
|
-
/* Developed collaboratively with vscode - 改动代码行数: 3行 */
|
|
2124
|
-
.yg-confirm[data-v-a65cb6d5] {
|
|
2125
|
-
display: inline-block;
|
|
2126
|
-
}
|
|
2127
|
-
|
|
2128
|
-
/* Developed collaboratively with vscode - 改动代码行数: 7行 */
|
|
2129
|
-
.base-card-wrapper[data-v-956107d8] {
|
|
2130
|
-
height: 100%;
|
|
2131
|
-
background: #f0f2f5;
|
|
2132
|
-
display: flex;
|
|
2133
|
-
flex-direction: column;
|
|
2134
|
-
}
|
|
2135
|
-
.dark .base-card-wrapper[data-v-956107d8] {
|
|
2136
|
-
background: #141414;
|
|
2137
|
-
}
|
|
2138
|
-
|
|
2139
|
-
/* Developed collaboratively with vscode - 改动代码行数: 6行 */
|
|
2140
|
-
.base-card-content[data-v-956107d8] {
|
|
2141
|
-
flex: 1;
|
|
2142
|
-
overflow: auto;
|
|
2143
|
-
padding: 16px;
|
|
2144
|
-
}
|
|
2145
|
-
/* NSpin 容器需继承 flex:1 撑满剩余空间 */
|
|
2146
|
-
.base-card-content[data-v-956107d8] .n-spin-container {
|
|
2147
|
-
height: 100%;
|
|
2148
|
-
}
|
|
2149
|
-
|
|
2150
|
-
/* Developed collaboratively with vscode - 改动代码行数: 10行 */
|
|
2151
|
-
/* BaseCardItem 之间的间距 */
|
|
2152
|
-
.base-card-content[data-v-956107d8] .base-card-item-spacing {
|
|
2153
|
-
margin-bottom: 16px !important;
|
|
2154
|
-
}
|
|
2155
|
-
.base-card-content[data-v-956107d8] .base-card-item-spacing:last-child {
|
|
2156
|
-
margin-bottom: 0 !important;
|
|
2157
|
-
}
|
|
2158
|
-
|
|
2159
|
-
/* Developed collaboratively with vscode - 改动代码行数: 20行 */
|
|
2160
|
-
.base-card-footer[data-v-956107d8] {
|
|
2161
|
-
flex-shrink: 0;
|
|
2162
|
-
height: 56px;
|
|
2163
|
-
background: #ffffff;
|
|
2164
|
-
box-shadow: 0 -8px 12px 0 rgba(0, 0, 0, 0.04);
|
|
2165
|
-
display: flex;
|
|
2166
|
-
align-items: center;
|
|
2167
|
-
justify-content: flex-end;
|
|
2168
|
-
padding: 0 24px;
|
|
2169
|
-
}
|
|
2170
|
-
.dark .base-card-footer[data-v-956107d8] {
|
|
2171
|
-
background: #1e1e1e;
|
|
2172
|
-
box-shadow: 0 -8px 12px 0 rgba(255, 255, 255, 0.04);
|
|
2173
|
-
}
|
|
2174
|
-
|
|
2175
2123
|
.base-card-item[data-v-e9115957] {
|
|
2176
2124
|
background: var(--n-color) !important;
|
|
2177
2125
|
padding: 0 !important;
|
|
@@ -2228,41 +2176,94 @@ to {
|
|
|
2228
2176
|
gap: 12px;
|
|
2229
2177
|
}
|
|
2230
2178
|
|
|
2231
|
-
|
|
2179
|
+
/* Developed collaboratively with vscode - 改动代码行数: 31行(折叠面板样式自 FlowLayout.vue 迁入:单个折叠项间距/标题行布局/标题字号与底边框/内容区留白) */
|
|
2180
|
+
.flow-node[data-v-25e5146e] {
|
|
2232
2181
|
/* 单个折叠项:去掉默认上外边距,上下间距统一由 header 内边距控制 */
|
|
2233
2182
|
}
|
|
2234
|
-
.flow-node[data-v-
|
|
2183
|
+
.flow-node[data-v-25e5146e] .n-collapse-item {
|
|
2235
2184
|
margin: 0;
|
|
2236
2185
|
}
|
|
2237
|
-
.flow-node[data-v-
|
|
2186
|
+
.flow-node[data-v-25e5146e] {
|
|
2238
2187
|
/* 标题行:文字左对齐、箭头右对齐;左右留白放在 header-main 上,保证 header 底边框顶满两侧 */
|
|
2239
2188
|
}
|
|
2240
|
-
.flow-node[data-v-
|
|
2189
|
+
.flow-node[data-v-25e5146e] .n-collapse-item__header-main {
|
|
2241
2190
|
justify-content: space-between;
|
|
2242
2191
|
padding: 0 16px;
|
|
2243
2192
|
}
|
|
2244
|
-
.flow-node[data-v-
|
|
2245
|
-
/*
|
|
2193
|
+
.flow-node[data-v-25e5146e] {
|
|
2194
|
+
/* 标题插槽包装:标题文案与状态标签横向排列、垂直居中;自身为 header-main 单一 flex 项,不参与 space-between 分离 */
|
|
2246
2195
|
}
|
|
2247
|
-
.flow-node .flow-node__title[data-v-
|
|
2196
|
+
.flow-node .flow-node__title[data-v-25e5146e] {
|
|
2248
2197
|
display: inline-flex;
|
|
2249
2198
|
align-items: center;
|
|
2250
2199
|
gap: 8px;
|
|
2251
2200
|
}
|
|
2252
|
-
.flow-node[data-v-
|
|
2201
|
+
.flow-node[data-v-25e5146e] {
|
|
2253
2202
|
/* 标题字号增大,上下等距内边距 + 底部分隔边框;用 first-child 提高优先级,覆盖 naive-ui 对首个折叠项 header 强制的 padding-top: 0 */
|
|
2254
2203
|
}
|
|
2255
|
-
.flow-node[data-v-
|
|
2204
|
+
.flow-node[data-v-25e5146e] .n-collapse-item:first-child > .n-collapse-item__header {
|
|
2256
2205
|
padding: 16px 0;
|
|
2257
2206
|
font-size: 16px;
|
|
2258
2207
|
border-bottom: 1px solid var(--n-divider-color, rgba(0, 0, 0, 0.09));
|
|
2259
2208
|
}
|
|
2260
|
-
.flow-node[data-v-
|
|
2209
|
+
.flow-node[data-v-25e5146e] {
|
|
2261
2210
|
/* 内容区留白与标题行对齐 */
|
|
2262
2211
|
}
|
|
2263
|
-
.flow-node[data-v-
|
|
2212
|
+
.flow-node[data-v-25e5146e] .n-collapse-item__content-wrapper {
|
|
2264
2213
|
padding: 0 16px 16px;
|
|
2265
2214
|
}
|
|
2215
|
+
/* Developed collaboratively with vscode - 改动代码行数: 3行 */
|
|
2216
|
+
.yg-confirm[data-v-a65cb6d5] {
|
|
2217
|
+
display: inline-block;
|
|
2218
|
+
}
|
|
2219
|
+
|
|
2220
|
+
/* Developed collaboratively with vscode - 改动代码行数: 7行 */
|
|
2221
|
+
.base-card-wrapper[data-v-956107d8] {
|
|
2222
|
+
height: 100%;
|
|
2223
|
+
background: #f0f2f5;
|
|
2224
|
+
display: flex;
|
|
2225
|
+
flex-direction: column;
|
|
2226
|
+
}
|
|
2227
|
+
.dark .base-card-wrapper[data-v-956107d8] {
|
|
2228
|
+
background: #141414;
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
/* Developed collaboratively with vscode - 改动代码行数: 6行 */
|
|
2232
|
+
.base-card-content[data-v-956107d8] {
|
|
2233
|
+
flex: 1;
|
|
2234
|
+
overflow: auto;
|
|
2235
|
+
padding: 16px;
|
|
2236
|
+
}
|
|
2237
|
+
/* NSpin 容器需继承 flex:1 撑满剩余空间 */
|
|
2238
|
+
.base-card-content[data-v-956107d8] .n-spin-container {
|
|
2239
|
+
height: 100%;
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
/* Developed collaboratively with vscode - 改动代码行数: 10行 */
|
|
2243
|
+
/* BaseCardItem 之间的间距 */
|
|
2244
|
+
.base-card-content[data-v-956107d8] .base-card-item-spacing {
|
|
2245
|
+
margin-bottom: 16px !important;
|
|
2246
|
+
}
|
|
2247
|
+
.base-card-content[data-v-956107d8] .base-card-item-spacing:last-child {
|
|
2248
|
+
margin-bottom: 0 !important;
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
/* Developed collaboratively with vscode - 改动代码行数: 20行 */
|
|
2252
|
+
.base-card-footer[data-v-956107d8] {
|
|
2253
|
+
flex-shrink: 0;
|
|
2254
|
+
height: 56px;
|
|
2255
|
+
background: #ffffff;
|
|
2256
|
+
box-shadow: 0 -8px 12px 0 rgba(0, 0, 0, 0.04);
|
|
2257
|
+
display: flex;
|
|
2258
|
+
align-items: center;
|
|
2259
|
+
justify-content: flex-end;
|
|
2260
|
+
padding: 0 24px;
|
|
2261
|
+
}
|
|
2262
|
+
.dark .base-card-footer[data-v-956107d8] {
|
|
2263
|
+
background: #1e1e1e;
|
|
2264
|
+
box-shadow: 0 -8px 12px 0 rgba(255, 255, 255, 0.04);
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2266
2267
|
.flow-item[data-v-aa89ccbf] {
|
|
2267
2268
|
display: flex;
|
|
2268
2269
|
align-items: center;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { FlowNodeItem } from './FlowNode';
|
|
2
|
+
import { FlowStatusTag, FlowTimelineData } from './timeline-mapping';
|
|
3
|
+
import { SelectedItem } from '../person-selector/types';
|
|
4
|
+
type __VLS_Props = {
|
|
5
|
+
/** 原始详情数据(display 场景推荐):内部映射为审批时间线并自动推导标题状态标签 */
|
|
6
|
+
data?: FlowTimelineData;
|
|
7
|
+
/** 已构建节点链:直接渲染(form 模式传请求所得节点链;display 场景传已映射时间线,未传 data 时生效,状态标签需配 statusTag) */
|
|
8
|
+
nodes?: FlowNodeItem[];
|
|
9
|
+
/** 渲染模式:display=审批时间线(缺省按 display 处理)/ form=发起节点(自选审批人为人员选择器、空态文案按模式区分) */
|
|
10
|
+
mode?: 'display' | 'form';
|
|
11
|
+
/** 节点区加载中:NSpin 遮罩(form 模式请求节点期间由外部传入,display 默认 false) */
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
/** 折叠面板标题 */
|
|
14
|
+
title?: string;
|
|
15
|
+
/** 标题状态标签:data 模式自动推导无需传;nodes 模式需外部传入 */
|
|
16
|
+
statusTag?: FlowStatusTag;
|
|
17
|
+
};
|
|
18
|
+
type __VLS_ModelProps = {
|
|
19
|
+
/** 自选审批人节点已选人员(key=节点名) */
|
|
20
|
+
'selectedUsersMap'?: Record<string, SelectedItem[]>;
|
|
21
|
+
};
|
|
22
|
+
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
23
|
+
declare const __VLS_export: import('vue').DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
|
|
24
|
+
"update:selectedUsersMap": (value: Record<string, SelectedItem[]>) => any;
|
|
25
|
+
}, string, import('vue').PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
|
|
26
|
+
"onUpdate:selectedUsersMap"?: ((value: Record<string, SelectedItem[]>) => any) | undefined;
|
|
27
|
+
}>, {
|
|
28
|
+
title: string;
|
|
29
|
+
loading: boolean;
|
|
30
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
31
|
+
declare const _default: typeof __VLS_export;
|
|
32
|
+
export default _default;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { FlowNodeItem, FlowNodeUserRecord } from './FlowNode';
|
|
2
|
+
import { WflowTodoDetail, WflowTodoRecord } from '../adapter/wflow';
|
|
3
|
+
/** display 模式原始详情数据:节点快照自带任务状态与每人处理结果 + status/finished 实例状态 */
|
|
4
|
+
export type FlowTimelineData = Pick<WflowTodoDetail, 'nodes' | 'finished' | 'status'>;
|
|
5
|
+
/** 流程状态标签(getFlowStatusTag 返回):颜色映射 已同意=success / 已拒绝=error / 进行中=warning */
|
|
6
|
+
export interface FlowStatusTag {
|
|
7
|
+
text: string;
|
|
8
|
+
type: 'success' | 'error' | 'warning';
|
|
9
|
+
}
|
|
10
|
+
/** 固定尾节点:节点有数据时渲染追加(display 下状态按流程是否走完覆盖),接口返回与提交数据均不包含 */
|
|
11
|
+
export declare const END_NODE: FlowNodeItem;
|
|
12
|
+
/** 人员处理记录映射:一行 records 对应一条展示记录(finished=0 未完成行过滤)——result 优先:result=2 同意;result=3 或 is_reject=1 驳回行按记录操作人判定方向(record.user_id=挂载人员 id 主动驳回「驳回到 rejecters.name」、否则被驳回「被 user_name 驳回」、名字均缺且 flowRejected=true 展示「驳回到 发起人」、否则「驳回」);result=0 不展示 */
|
|
13
|
+
export declare function toUserRecords(user: {
|
|
14
|
+
id?: number;
|
|
15
|
+
records?: WflowTodoRecord[];
|
|
16
|
+
}, flowRejected?: boolean): FlowNodeUserRecord[];
|
|
17
|
+
/** 已处理记录(result=2/3 或 is_reject=1,与 toUserRecords 口径一致)中处理时间(assertion_time 毫秒时间戳)最新的一条是否为驳回(result=3 或 is_reject=1);空集合返回 false,records 数组顺序不可靠必须按时间数值比较 */
|
|
18
|
+
export declare function isLatestRowsReject(rows: WflowTodoRecord[]): boolean;
|
|
19
|
+
/** display 模式审批时间线映射:节点快照直接映射 FlowNodeItem——发起人节点独立判定:流程被驳回到发起人(finished=1 且 status=-1)标红、其余一律完成绿色;其余节点按实例 finished、节点自带 finished(1=已完成)推导:进行中节点优先取第一个「未完成且 status=1 待处理」的节点,无激活节点时兜底认第一个存在待处理任务行(users.records 中 result=0 且 finished=0)的节点为当前节点,仍无则回退第一个未完成节点,有激活/兜底节点时其后节点一律待开始(剥离上一轮残留),流程被驳回终止时其余节点一律灰色待开始;人员状态按本人最新一条操作记录展示(已处理记录 result=2/3 或 is_reject=1 中 assertion_time 最新一条:驳回标红、同意标绿),无操作记录随节点状态(进行中节点淡黄、其余灰色未处理) */
|
|
20
|
+
export declare function mapTimelineNodes(data?: FlowTimelineData): FlowNodeItem[];
|
|
21
|
+
/** 组装完整节点链:有数据统一追加 END 结束节点,无数据为空数组走空态;display(非 form)流程全部完成时 END 状态同步 done;末节点已是 end(后端快照自带 end、或调用方传入已组装过 END 的节点链,如 FlowLayout → FlowNodesPanel)时幂等跳过,防止结束节点重复渲染 */
|
|
22
|
+
export declare function withEndNode(baseNodes: FlowNodeItem[], mode?: 'display' | 'form'): FlowNodeItem[];
|
|
23
|
+
/** 折叠面板标题流程状态标签:仅 display 模式且有详情数据时推导——finished=1 且 status=-1 已拒绝(驳回到发起人终止)、finished=1 已同意、未结束进行中;form 发起模式无实例状态不展示,数据未返回前隐藏避免先展示「进行中」再跳变 */
|
|
24
|
+
export declare function getFlowStatusTag(mode: 'display' | 'form' | undefined, data?: FlowTimelineData): FlowStatusTag | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yugu-ui/flow-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "堉古商城流程组件库:流程设计器(config)/ 审批时间线(timeline)/ 流程列表(FlowList)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -20,10 +20,6 @@
|
|
|
20
20
|
},
|
|
21
21
|
"./style.css": "./dist/style.css"
|
|
22
22
|
},
|
|
23
|
-
"scripts": {
|
|
24
|
-
"build": "vite build --config vite.lib.config.ts",
|
|
25
|
-
"typecheck": "vue-tsc --noEmit --skipLibCheck"
|
|
26
|
-
},
|
|
27
23
|
"peerDependencies": {
|
|
28
24
|
"naive-ui": ">=2.40.0",
|
|
29
25
|
"vue": ">=3.3.0"
|
|
@@ -54,5 +50,9 @@
|
|
|
54
50
|
"publishConfig": {
|
|
55
51
|
"access": "public",
|
|
56
52
|
"registry": "https://registry.npmjs.org/"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "vite build --config vite.lib.config.ts",
|
|
56
|
+
"typecheck": "vue-tsc --noEmit --skipLibCheck"
|
|
57
57
|
}
|
|
58
|
-
}
|
|
58
|
+
}
|