From a9d97b150701e634bdb751eab277696abd136cca Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 16 六月 2026 14:39:47 +0800
Subject: [PATCH] 君歌app 1.依照web端功能修改
---
src/pages/oa/ReimburseManage/_components/ReimburseApprovalFlowEditor.vue | 245 +++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 245 insertions(+), 0 deletions(-)
diff --git a/src/pages/oa/ReimburseManage/_components/ReimburseApprovalFlowEditor.vue b/src/pages/oa/ReimburseManage/_components/ReimburseApprovalFlowEditor.vue
new file mode 100644
index 0000000..77bd712
--- /dev/null
+++ b/src/pages/oa/ReimburseManage/_components/ReimburseApprovalFlowEditor.vue
@@ -0,0 +1,245 @@
+<!--
+ 鎶ラ攢瀹℃壒娴佺▼锛堝彲鎼滅储閫変汉锛岀偣閫夊嵆纭锛�
+-->
+<template>
+ <view class="flow-wrap">
+ <view v-for="(item, index) in innerList"
+ :key="item._uid"
+ class="flow-node-block">
+ <view class="flow-node-card">
+ <view class="node-header">
+ <view class="node-level-badge">{{ index + 1 }}</view>
+ <text class="node-level-text">绗瑊{ levelLabel(index + 1) }}绾у鎵�</text>
+ <view v-if="innerList.length > 1"
+ class="node-delete"
+ @click="remove(index)">
+ <up-icon name="trash"
+ size="16"
+ color="#f56c6c" />
+ </view>
+ </view>
+ <view class="approver-row"
+ @click="openPicker(index)">
+ <view class="approver-avatar"
+ :style="{ backgroundColor: avatarColor(item.approverName) }">
+ {{ (item.approverName || '+').charAt(0) }}
+ </view>
+ <view class="approver-meta">
+ <text class="approver-name">{{ item.approverName || '鐐瑰嚮閫夋嫨瀹℃壒浜�' }}</text>
+ <text class="approver-hint">鏀寔鎼滅储濮撳悕鎴栧伐鍙�</text>
+ </view>
+ <up-icon name="arrow-right"
+ size="14"
+ color="#c0c4cc" />
+ </view>
+ </view>
+ <view v-if="index < innerList.length - 1"
+ class="flow-connector">
+ <view class="flow-connector-line" />
+ </view>
+ </view>
+ <view class="add-node-bar"
+ @click="addNode">
+ <up-icon name="plus-circle"
+ size="18"
+ color="#2979ff" />
+ <text>娣诲姞瀹℃壒绾ф</text>
+ </view>
+
+ <OaUserSearchPicker v-model:show="pickerShow"
+ v-model="pickerUserId"
+ title="閫夋嫨瀹℃壒浜�"
+ :users="userOptions"
+ :show-self-quick="false"
+ @select="onUserSelected" />
+ </view>
+</template>
+
+<script setup>
+ import { ref, watch } from "vue";
+ import OaUserSearchPicker from "../../_components/OaUserSearchPicker.vue";
+ import { userAvatarColor } from "../../_utils/userPickerUtils.js";
+
+ const props = defineProps({
+ modelValue: { type: Array, default: () => [] },
+ userOptions: { type: Array, default: () => [] },
+ });
+ const emit = defineEmits(["update:modelValue"]);
+
+ const innerList = ref([]);
+ const pickerShow = ref(false);
+ const pickerUserId = ref("");
+ const editingIndex = ref(-1);
+
+ function newUid() {
+ return `n_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
+ }
+
+ function levelLabel(n) {
+ const t = ["涓�", "浜�", "涓�", "鍥�", "浜�", "鍏�", "涓�", "鍏�"];
+ return t[n - 1] || String(n);
+ }
+
+ function avatarColor(name) {
+ return userAvatarColor(name);
+ }
+
+ function mapIn(rows) {
+ return (rows || []).map((n, i) => ({
+ _uid: n._uid || newUid(),
+ nodeOrder: n.nodeOrder ?? i + 1,
+ signMode: n.signMode || "countersign",
+ approverId: n.approverId ?? "",
+ approverName: n.approverName || "",
+ id: n.id,
+ templateId: n.templateId,
+ }));
+ }
+
+ function mapOut() {
+ return innerList.value.map((n, i) => ({
+ nodeOrder: i + 1,
+ signMode: n.signMode || "countersign",
+ approverId: n.approverId,
+ approverName: n.approverName,
+ id: n.id,
+ templateId: n.templateId,
+ }));
+ }
+
+ function syncEmit() {
+ emit("update:modelValue", mapOut());
+ }
+
+ watch(
+ () => props.modelValue,
+ v => {
+ innerList.value = mapIn(v);
+ if (!innerList.value.length) {
+ innerList.value = [
+ { _uid: newUid(), nodeOrder: 1, signMode: "countersign", approverId: "", approverName: "" },
+ ];
+ }
+ },
+ { immediate: true, deep: true }
+ );
+
+ function addNode() {
+ innerList.value.push({
+ _uid: newUid(),
+ nodeOrder: innerList.value.length + 1,
+ signMode: "countersign",
+ approverId: "",
+ approverName: "",
+ });
+ syncEmit();
+ }
+
+ function remove(index) {
+ if (innerList.value.length <= 1) {
+ uni.showToast({ title: "鑷冲皯淇濈暀涓�涓鎵硅妭鐐�", icon: "none" });
+ return;
+ }
+ innerList.value.splice(index, 1);
+ syncEmit();
+ }
+
+ function openPicker(index) {
+ editingIndex.value = index;
+ pickerUserId.value = innerList.value[index]?.approverId || "";
+ pickerShow.value = true;
+ }
+
+ function onUserSelected(u) {
+ const node = innerList.value[editingIndex.value];
+ if (!node) return;
+ node.approverId = u.userId ?? u.id;
+ node.approverName = u.nickName || u.userName || "";
+ syncEmit();
+ }
+</script>
+
+<style scoped lang="scss">
+ .flow-node-card {
+ background: #f8f9fb;
+ border-radius: 10px;
+ padding: 12px;
+ border: 1px solid #eef0f3;
+ }
+ .node-header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 10px;
+ }
+ .node-level-badge {
+ width: 22px;
+ height: 22px;
+ border-radius: 50%;
+ background: #2979ff;
+ color: #fff;
+ font-size: 12px;
+ text-align: center;
+ line-height: 22px;
+ margin-right: 8px;
+ }
+ .node-level-text {
+ flex: 1;
+ font-size: 14px;
+ color: #303133;
+ font-weight: 500;
+ }
+ .approver-row {
+ display: flex;
+ align-items: center;
+ padding: 10px 12px;
+ background: #fff;
+ border-radius: 8px;
+ }
+ .approver-avatar {
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ color: #fff;
+ font-size: 15px;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ }
+ .approver-meta {
+ flex: 1;
+ margin-left: 10px;
+ min-width: 0;
+ }
+ .approver-name {
+ display: block;
+ font-size: 15px;
+ color: #303133;
+ }
+ .approver-hint {
+ display: block;
+ font-size: 12px;
+ color: #c0c4cc;
+ margin-top: 2px;
+ }
+ .flow-connector {
+ display: flex;
+ justify-content: center;
+ padding: 6px 0;
+ }
+ .flow-connector-line {
+ width: 2px;
+ height: 14px;
+ background: #dcdfe6;
+ }
+ .add-node-bar {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ padding: 14px 0 4px;
+ color: #2979ff;
+ font-size: 14px;
+ }
+</style>
--
Gitblit v1.9.3