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/ReimburseExpenseDetailSheet.vue |  315 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 315 insertions(+), 0 deletions(-)

diff --git a/src/pages/oa/ReimburseManage/_components/ReimburseExpenseDetailSheet.vue b/src/pages/oa/ReimburseManage/_components/ReimburseExpenseDetailSheet.vue
new file mode 100644
index 0000000..a0b25f0
--- /dev/null
+++ b/src/pages/oa/ReimburseManage/_components/ReimburseExpenseDetailSheet.vue
@@ -0,0 +1,315 @@
+<!--
+  鎶ラ攢鏄庣粏鍗曟潯缂栬緫锛堝簳閮ㄥ脊灞傦級
+-->
+<template>
+  <up-popup :show="show"
+            mode="bottom"
+            round="16"
+            :safe-area-inset-bottom="true"
+            @close="close">
+    <view class="detail-sheet">
+      <view class="sheet-handle" />
+      <view class="sheet-head">
+        <text class="sheet-cancel"
+              @click="close">鍙栨秷</text>
+        <text class="sheet-title">{{ title }}</text>
+        <text class="sheet-confirm"
+              @click="confirm">淇濆瓨</text>
+      </view>
+
+      <scroll-view scroll-y
+                   class="sheet-body"
+                   :show-scrollbar="false">
+        <view class="sheet-group">
+          <view class="sheet-cell sheet-cell--tap"
+                @click="showDatePicker = true">
+            <text class="sheet-label required">鍙戠エ鏃ユ湡</text>
+            <view class="sheet-value-wrap">
+              <text class="sheet-value"
+                    :class="{ placeholder: !draft.invoiceDate }">
+                {{ draft.invoiceDate || '璇烽�夋嫨' }}
+              </text>
+              <up-icon name="calendar"
+                       size="18"
+                       color="#c0c4cc" />
+            </view>
+          </view>
+          <view class="sheet-cell sheet-cell--tap"
+                @click="showSubjectSheet = true">
+            <text class="sheet-label required">璐圭敤绉戠洰</text>
+            <view class="sheet-value-wrap">
+              <text class="sheet-value"
+                    :class="{ placeholder: !draft.expenseSubject }">
+                {{ subjectText }}
+              </text>
+              <up-icon name="arrow-right"
+                       size="14"
+                       color="#c0c4cc" />
+            </view>
+          </view>
+          <view class="sheet-cell">
+            <text class="sheet-label required">閲戦</text>
+            <view class="sheet-input-wrap">
+              <up-input v-model="draft.amount"
+                        type="digit"
+                        placeholder="0.00"
+                        border="none"
+                        input-align="right" />
+              <text class="sheet-unit">鍏�</text>
+            </view>
+          </view>
+          <view class="sheet-cell sheet-cell--col">
+            <text class="sheet-label">鎻忚堪</text>
+            <view class="sheet-textarea-wrap">
+              <up-textarea v-model="draft.description"
+                           placeholder="璐圭敤璇存槑锛堥�夊~锛�"
+                           maxlength="200"
+                           border="none"
+                           height="64" />
+            </view>
+          </view>
+        </view>
+
+        <view v-if="showDelete"
+              class="sheet-delete"
+              @click="emit('delete')">
+          鍒犻櫎鏈潯鏄庣粏
+        </view>
+      </scroll-view>
+    </view>
+
+    <up-action-sheet :show="showSubjectSheet"
+                     title="璐圭敤绉戠洰"
+                     :actions="subjectActions"
+                     @select="onSubjectSelect"
+                     @close="showSubjectSheet = false" />
+
+    <up-popup :show="showDatePicker"
+              mode="bottom"
+              round="16"
+              @close="showDatePicker = false">
+      <up-datetime-picker :show="true"
+                          v-model="datePickerTs"
+                          mode="date"
+                          @confirm="onDateConfirm"
+                          @cancel="showDatePicker = false" />
+    </up-popup>
+  </up-popup>
+</template>
+
+<script setup>
+  import { computed, reactive, ref, watch } from "vue";
+  import { parseTime } from "@/utils/ruoyi";
+  import { expenseSubjectLabel as costSubjectLabel } from "../_utils/costReimburseUtils.js";
+  import { expenseSubjectLabel as travelSubjectLabel } from "../_utils/travelReimburseUtils.js";
+
+  const props = defineProps({
+    show: { type: Boolean, default: false },
+    modelValue: { type: Object, default: () => ({}) },
+    index: { type: Number, default: 0 },
+    isTravel: { type: Boolean, default: true },
+    subjectOptions: { type: Array, default: () => [] },
+    showDelete: { type: Boolean, default: true },
+  });
+
+  const emit = defineEmits(["update:show", "update:modelValue", "confirm", "delete"]);
+
+  const draft = reactive({
+    invoiceDate: "",
+    expenseSubject: "",
+    amount: "",
+    description: "",
+  });
+
+  const showDatePicker = ref(false);
+  const showSubjectSheet = ref(false);
+  const datePickerTs = ref(Date.now());
+
+  const title = computed(() => `鏄庣粏 ${props.index + 1}`);
+
+  const subjectActions = computed(() =>
+    (props.subjectOptions || []).map(x => ({ name: x.label, value: x.value }))
+  );
+
+  const subjectText = computed(() => resolveSubjectLabel(draft.expenseSubject));
+
+  function resolveSubjectLabel(v) {
+    if (!v) return "璇烽�夋嫨";
+    const labelFn = props.isTravel ? travelSubjectLabel : costSubjectLabel;
+    const t = labelFn(v);
+    if (t && t !== "鈥�") return t;
+    const hit = (props.subjectOptions || []).find(x => x.value === v || x.label === v);
+    return hit?.label || v;
+  }
+
+  watch(
+    () => props.show,
+    v => {
+      if (v && props.modelValue) {
+        Object.assign(draft, {
+          invoiceDate: "",
+          expenseSubject: "",
+          amount: "",
+          description: "",
+          ...JSON.parse(JSON.stringify(props.modelValue)),
+        });
+      }
+    }
+  );
+
+  function close() {
+    emit("update:show", false);
+  }
+
+  function confirm() {
+    if (!draft.invoiceDate) {
+      uni.showToast({ title: "璇烽�夋嫨鍙戠エ鏃ユ湡", icon: "none" });
+      return;
+    }
+    if (!draft.expenseSubject) {
+      uni.showToast({ title: "璇烽�夋嫨璐圭敤绉戠洰", icon: "none" });
+      return;
+    }
+    if (draft.amount === "" || draft.amount == null) {
+      uni.showToast({ title: "璇峰~鍐欓噾棰�", icon: "none" });
+      return;
+    }
+    emit("update:modelValue", { ...draft });
+    emit("confirm", { ...draft });
+    emit("update:show", false);
+  }
+
+  function onSubjectSelect(action) {
+    draft.expenseSubject = action.value;
+    showSubjectSheet.value = false;
+  }
+
+  function onDateConfirm(e) {
+    const ts = e?.value ?? datePickerTs.value;
+    draft.invoiceDate = parseTime(ts, "{y}-{m}-{d}");
+    showDatePicker.value = false;
+  }
+</script>
+
+<style scoped lang="scss">
+  .detail-sheet {
+    background: #fff;
+    border-radius: 16px 16px 0 0;
+    max-height: 85vh;
+    display: flex;
+    flex-direction: column;
+  }
+  .sheet-handle {
+    width: 36px;
+    height: 4px;
+    background: #e4e7ed;
+    border-radius: 2px;
+    margin: 8px auto 4px;
+  }
+  .sheet-head {
+    display: flex;
+    align-items: center;
+    padding: 8px 16px 12px;
+    border-bottom: 1px solid #f0f2f5;
+  }
+  .sheet-cancel {
+    font-size: 15px;
+    color: #909399;
+    min-width: 48px;
+  }
+  .sheet-title {
+    flex: 1;
+    text-align: center;
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+  }
+  .sheet-confirm {
+    font-size: 15px;
+    color: #2979ff;
+    font-weight: 600;
+    min-width: 48px;
+    text-align: right;
+  }
+  .sheet-body {
+    max-height: 70vh;
+    padding-bottom: env(safe-area-inset-bottom);
+  }
+  .sheet-group {
+    margin: 12px 16px;
+    background: #f8f9fb;
+    border-radius: 12px;
+    overflow: hidden;
+  }
+  .sheet-cell {
+    display: flex;
+    align-items: center;
+    min-height: 52px;
+    padding: 12px 14px;
+    background: #fff;
+    border-bottom: 1px solid #f5f6f8;
+    &--col {
+      flex-direction: column;
+      align-items: stretch;
+    }
+    &--tap:active {
+      background: #fafbfc;
+    }
+    &:last-child {
+      border-bottom: none;
+    }
+  }
+  .sheet-label {
+    width: 80px;
+    font-size: 15px;
+    color: #303133;
+    flex-shrink: 0;
+    &.required::before {
+      content: "*";
+      color: #f56c6c;
+      margin-right: 2px;
+    }
+  }
+  .sheet-value-wrap {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+    gap: 4px;
+  }
+  .sheet-value {
+    font-size: 15px;
+    color: #303133;
+    &.placeholder {
+      color: #c0c4cc;
+    }
+  }
+  .sheet-input-wrap {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+  }
+  .sheet-unit {
+    font-size: 14px;
+    color: #909399;
+    margin-left: 4px;
+  }
+  .sheet-textarea-wrap {
+    width: 100%;
+    margin-top: 8px;
+    background: #f5f7fa;
+    border-radius: 8px;
+    padding: 4px 8px;
+  }
+  .sheet-delete {
+    margin: 16px;
+    text-align: center;
+    font-size: 15px;
+    color: #f56c6c;
+    padding: 14px;
+    background: #fff;
+    border-radius: 12px;
+    border: 1px solid #fde2e2;
+  }
+</style>

--
Gitblit v1.9.3