|
@@ -12,59 +12,397 @@
|
|
|
><view v-for="(row, i) in group.rows" :key="row.label"
|
|
><view v-for="(row, i) in group.rows" :key="row.label"
|
|
|
><view class="info-row press" @click="row.edit && editRow(row)"
|
|
><view class="info-row press" @click="row.edit && editRow(row)"
|
|
|
><text>{{ row.label }}</text
|
|
><text>{{ row.label }}</text
|
|
|
- ><text>{{ row.value }}</text
|
|
|
|
|
|
|
+ ><text>{{ resolveValue(row) }}</text
|
|
|
><text v-if="row.edit" class="arrow">›</text></view
|
|
><text v-if="row.edit" class="arrow">›</text></view
|
|
|
><view
|
|
><view
|
|
|
v-if="i < group.rows.length - 1"
|
|
v-if="i < group.rows.length - 1"
|
|
|
class="divider"
|
|
class="divider"
|
|
|
></view></view></view></view
|
|
></view></view></view></view
|
|
|
- ><view v-if="lastRequest" class="request-tip card section"
|
|
|
|
|
- ><text>最近修改申请</text
|
|
|
|
|
- ><text>{{ lastRequest.field }}:{{ lastRequest.value }}</text
|
|
|
|
|
- ><text class="status-tag status-pending">审核中</text></view
|
|
|
|
|
- ><text class="tip">点击可修改字段,提交后由人力资源部审核生效</text></view
|
|
|
|
|
|
|
+ ><view class="group-title"
|
|
|
|
|
+ ><text>银行卡({{ bankCardList.length }})</text
|
|
|
|
|
+ ><text class="add-bank press" @click="addBankCard">+ 添加</text></view
|
|
|
|
|
+ ><view v-if="bankCardList.length" class="bank-card-list"
|
|
|
|
|
+ ><view v-for="card in bankCardList" :key="card.id" class="bank-card card section"
|
|
|
|
|
+ ><view class="bank-head"
|
|
|
|
|
+ ><view class="bank-title"
|
|
|
|
|
+ ><text class="bank-name">{{ card.openingBank || "未命名银行" }}</text></view
|
|
|
|
|
+ ><view class="bank-actions"
|
|
|
|
|
+ ><text class="bank-action press" @click="editBankCard(card)">编辑</text
|
|
|
|
|
+ ><text class="bank-action danger press" @click="removeBankCard(card)">删除</text></view
|
|
|
|
|
+ ></view
|
|
|
|
|
+ ><view class="bank-no">{{ formatCardNumber(card.cardNumber) }}</view
|
|
|
|
|
+ ><view v-if="card.branch" class="bank-branch">开户行:{{ card.branch }}</view
|
|
|
|
|
+ ></view
|
|
|
|
|
+ ></view
|
|
|
|
|
+ ><view v-else class="bank-empty card section"
|
|
|
|
|
+ ><text>暂未绑定银行卡</text
|
|
|
|
|
+ ><text class="press" @click="addBankCard">点击添加</text></view
|
|
|
|
|
+ ><text class="tip">点击可修改字段,提交后由人力资源部审核生效</text
|
|
|
|
|
+ ><view v-if="twoModal.visible" class="two-modal-mask" @click.self="cancelTwoModal"
|
|
|
|
|
+ ><view class="two-modal"
|
|
|
|
|
+ ><text class="two-modal-title">{{ twoModal.title }}</text
|
|
|
|
|
+ ><view class="two-modal-fields"
|
|
|
|
|
+ ><view v-for="field in twoModal.fields" :key="field.key" class="two-modal-field"
|
|
|
|
|
+ ><text class="two-modal-label">{{ field.label }}</text
|
|
|
|
|
+ ><input
|
|
|
|
|
+ class="two-modal-input"
|
|
|
|
|
+ :type="field.inputType || 'text'"
|
|
|
|
|
+ :maxlength="field.maxlength || 50"
|
|
|
|
|
+ v-model="twoModal.values[field.key]"
|
|
|
|
|
+ :placeholder="field.placeholder || ''"
|
|
|
|
|
+ /></view
|
|
|
|
|
+ ></view
|
|
|
|
|
+ ><view class="two-modal-actions"
|
|
|
|
|
+ ><text class="two-modal-btn press" @click="cancelTwoModal">取消</text
|
|
|
|
|
+ ><text class="two-modal-btn primary press" @click="confirmTwoModal">确认</text></view
|
|
|
|
|
+ ></view
|
|
|
|
|
+ ></view
|
|
|
|
|
+ ></view
|
|
|
>
|
|
>
|
|
|
</template>
|
|
</template>
|
|
|
<script setup>
|
|
<script setup>
|
|
|
import { computed, ref } from "vue";
|
|
import { computed, ref } from "vue";
|
|
|
import { useAuthGuard } from "@/hooks/useAuthGuard";
|
|
import { useAuthGuard } from "@/hooks/useAuthGuard";
|
|
|
import { useCurrentUser } from "@/hooks/useCurrentUser";
|
|
import { useCurrentUser } from "@/hooks/useCurrentUser";
|
|
|
-import { saveProfileChange } from "@/utils/storage";
|
|
|
|
|
|
|
+import { refetchMyProfile, saveEmployee } from "@/api/user";
|
|
|
const currentUser = useCurrentUser();
|
|
const currentUser = useCurrentUser();
|
|
|
useAuthGuard();
|
|
useAuthGuard();
|
|
|
-const lastRequest = ref(null);
|
|
|
|
|
|
|
+const saving = ref(false);
|
|
|
|
|
+// 银行卡:本地可编辑数组,先克隆接口数据,避免直接改 reactive 引用
|
|
|
|
|
+const bankCardList = ref(
|
|
|
|
|
+ (currentUser.bankCardList || []).map((c) => ({ ...c })),
|
|
|
|
|
+);
|
|
|
|
|
+
|
|
|
|
|
+function resolveValue(row) {
|
|
|
|
|
+ if (row.editor === "bank") {
|
|
|
|
|
+ const card = bankCardList.value[0];
|
|
|
|
|
+ return card ? `${card.openingBank} · 尾号 ${tailOf(card.cardNumber)}` : "未绑定";
|
|
|
|
|
+ }
|
|
|
|
|
+ return row.value;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function tailOf(cardNumber) {
|
|
|
|
|
+ const digits = String(cardNumber || "").replace(/\D/g, "");
|
|
|
|
|
+ return digits ? digits.slice(-4) : "----";
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function formatCardNumber(cardNumber) {
|
|
|
|
|
+ const digits = String(cardNumber || "").replace(/\D/g, "");
|
|
|
|
|
+ return digits.replace(/(.{4})/g, "$1 ").trim() || "----";
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function bankLabel(card) {
|
|
|
|
|
+ return `${card.openingBank} · 尾号 ${tailOf(card.cardNumber)}`;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const groups = computed(() => [
|
|
const groups = computed(() => [
|
|
|
{
|
|
{
|
|
|
title: "任职信息",
|
|
title: "任职信息",
|
|
|
rows: [
|
|
rows: [
|
|
|
{ label: "员工编号", value: currentUser.id },
|
|
{ label: "员工编号", value: currentUser.id },
|
|
|
{ label: "入职日期", value: currentUser.entryDate },
|
|
{ label: "入职日期", value: currentUser.entryDate },
|
|
|
- { label: "工作年限", value: currentUser.workAge },
|
|
|
|
|
|
|
+ // { label: "工作年限", value: currentUser.workAge },
|
|
|
{ label: "劳动合同", value: "固定期限 · 2028-02-12到期" },
|
|
{ label: "劳动合同", value: "固定期限 · 2028-02-12到期" },
|
|
|
],
|
|
],
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
title: "个人信息",
|
|
title: "个人信息",
|
|
|
rows: [
|
|
rows: [
|
|
|
- { label: "手机号码", value: currentUser.phone, edit: true },
|
|
|
|
|
- { label: "婚姻状况", value: "已婚", edit: true },
|
|
|
|
|
- { label: "紧急联系人", value: "陈志刚 · 138 **** 0903", edit: true },
|
|
|
|
|
- { label: "银行卡", value: "中国工商银行 · 尾号 8621", edit: true },
|
|
|
|
|
|
|
+ { label: "手机号码", value: currentUser.phone, edit: true, editor: "phone" },
|
|
|
|
|
+ { label: "婚姻状况", value: currentUser.maritalStatus || "未填写", edit: true, editor: "marital" },
|
|
|
|
|
+ { label: "紧急联系人", value: currentUser.emergencyContact || "暂未填写", edit: true, editor: "emergency" },
|
|
|
],
|
|
],
|
|
|
},
|
|
},
|
|
|
]);
|
|
]);
|
|
|
|
|
+const MARITAL_OPTIONS = ["未婚", "已婚", "离异", "丧偶"];
|
|
|
|
|
+
|
|
|
|
|
+// ============ 通用双输入弹框 ============
|
|
|
|
|
+const twoModal = ref({
|
|
|
|
|
+ visible: false,
|
|
|
|
|
+ title: "",
|
|
|
|
|
+ fields: [],
|
|
|
|
|
+ values: {},
|
|
|
|
|
+ resolver: null,
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+function openTwoModal({ title, fields, initial = {} }) {
|
|
|
|
|
+ return new Promise((resolve) => {
|
|
|
|
|
+ const values = {}
|
|
|
|
|
+ fields.forEach((f) => {
|
|
|
|
|
+ values[f.key] = String(initial[f.key] ?? "")
|
|
|
|
|
+ })
|
|
|
|
|
+ twoModal.value = {
|
|
|
|
|
+ visible: true,
|
|
|
|
|
+ title,
|
|
|
|
|
+ fields,
|
|
|
|
|
+ values,
|
|
|
|
|
+ resolver: resolve,
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function confirmTwoModal() {
|
|
|
|
|
+ const m = twoModal.value
|
|
|
|
|
+ if (!m.resolver) return
|
|
|
|
|
+ // 把 values 里每个字段裁掉首尾空白
|
|
|
|
|
+ const result = {}
|
|
|
|
|
+ m.fields.forEach((f) => {
|
|
|
|
|
+ result[f.key] = String(m.values[f.key] || "").trim()
|
|
|
|
|
+ })
|
|
|
|
|
+ m.resolver(result)
|
|
|
|
|
+ twoModal.value = { visible: false, title: "", fields: [], values: {}, resolver: null }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function cancelTwoModal() {
|
|
|
|
|
+ const m = twoModal.value
|
|
|
|
|
+ if (m.resolver) m.resolver(null)
|
|
|
|
|
+ twoModal.value = { visible: false, title: "", fields: [], values: {}, resolver: null }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function formatEmergency(name, phone, relation) {
|
|
|
|
|
+ const parts = []
|
|
|
|
|
+ if (name && relation) parts.push(`${name}(${relation})`)
|
|
|
|
|
+ else if (name) parts.push(name)
|
|
|
|
|
+ if (phone) parts.push(phone)
|
|
|
|
|
+ return parts.join(" · ")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 把 profile 接口返回的完整 VO 摊平后写入 currentUser(reactive)
|
|
|
|
|
+// 入参是 fetchUserDetail 摊平过的 raw 字段集(与 mapCurrentUser 输入一致)
|
|
|
|
|
+function applyProfileVo(vo) {
|
|
|
|
|
+ if (!vo || typeof vo !== "object") return
|
|
|
|
|
+ // 字符串字段
|
|
|
|
|
+ if ("phone" in vo) currentUser.phone = vo.phone || ""
|
|
|
|
|
+ if ("maritalStatus" in vo) currentUser.maritalStatus = vo.maritalStatus || ""
|
|
|
|
|
+ // 紧急联系人:从 secondLink* 拆出 name / phone / relation 三个扁平字段
|
|
|
|
|
+ if ("secondLinkName" in vo || "secondLinkPhone" in vo || "secondLinkRelation" in vo) {
|
|
|
|
|
+ currentUser.emergencyName = vo.secondLinkName || ""
|
|
|
|
|
+ currentUser.emergencyPhone = vo.secondLinkPhone || ""
|
|
|
|
|
+ currentUser.emergencyRelation = vo.secondLinkRelation || ""
|
|
|
|
|
+ currentUser.emergencyContact = formatEmergency(
|
|
|
|
|
+ vo.secondLinkName,
|
|
|
|
|
+ vo.secondLinkPhone,
|
|
|
|
|
+ vo.secondLinkRelation,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ // 银行卡列表:替换整个数组
|
|
|
|
|
+ if (Array.isArray(vo.bankCardList)) {
|
|
|
|
|
+ const next = vo.bankCardList.map((c) => ({
|
|
|
|
|
+ id: String(c.id ?? c.cardId ?? c.cardNumber ?? ""),
|
|
|
|
|
+ cardNumber: String(c.cardNumber || ""),
|
|
|
|
|
+ openingBank: String(c.openingBank || ""),
|
|
|
|
|
+ branch: String(c.branch || c.subBranch || ""),
|
|
|
|
|
+ }))
|
|
|
|
|
+ currentUser.bankCardList = next
|
|
|
|
|
+ bankCardList.value = next.map((c) => ({ ...c }))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 乐观合并:saveNew 接口响应里 data 通常只是 {id},
|
|
|
|
|
+// 不一定能拿到完整字段,所以这里用"调用方已知的本次修改 payload"直接落到 currentUser
|
|
|
|
|
+// - payload 是 saveEmployee 收到的入参对象(如 { phone }, { maritalStatus }, { bankCardList })
|
|
|
|
|
+function applyOptimistic(payload) {
|
|
|
|
|
+ if (!payload || typeof payload !== "object") return
|
|
|
|
|
+ if ("phone" in payload) currentUser.phone = payload.phone || ""
|
|
|
|
|
+ if ("maritalStatus" in payload) currentUser.maritalStatus = payload.maritalStatus || ""
|
|
|
|
|
+ if ("secondLinkName" in payload) currentUser.emergencyName = payload.secondLinkName || ""
|
|
|
|
|
+ if ("secondLinkPhone" in payload) currentUser.emergencyPhone = payload.secondLinkPhone || ""
|
|
|
|
|
+ if ("secondLinkRelation" in payload) currentUser.emergencyRelation = payload.secondLinkRelation || ""
|
|
|
|
|
+ if ("secondLinkName" in payload || "secondLinkPhone" in payload || "secondLinkRelation" in payload) {
|
|
|
|
|
+ currentUser.emergencyContact = formatEmergency(
|
|
|
|
|
+ currentUser.emergencyName,
|
|
|
|
|
+ currentUser.emergencyPhone,
|
|
|
|
|
+ currentUser.emergencyRelation,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ if (Array.isArray(payload.bankCardList)) {
|
|
|
|
|
+ const next = payload.bankCardList.map((c) => ({
|
|
|
|
|
+ id: String(c.id ?? c.cardId ?? c.cardNumber ?? ""),
|
|
|
|
|
+ cardNumber: String(c.cardNumber || ""),
|
|
|
|
|
+ openingBank: String(c.openingBank || ""),
|
|
|
|
|
+ branch: String(c.branch || ""),
|
|
|
|
|
+ }))
|
|
|
|
|
+ currentUser.bankCardList = next
|
|
|
|
|
+ bankCardList.value = next.map((c) => ({ ...c }))
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 统一提交入口:手机号 / 婚姻 / 紧急联系人 / 银行卡(增删改)都走这里
|
|
|
|
|
+async function commitChange(payload, { toast = "已保存" } = {}) {
|
|
|
|
|
+ if (saving.value) return
|
|
|
|
|
+ saving.value = true
|
|
|
|
|
+ uni.showLoading({ title: "保存中..." })
|
|
|
|
|
+ try {
|
|
|
|
|
+ await saveEmployee(payload)
|
|
|
|
|
+ // 先用本地 payload 做乐观合并(saveNew 通常只返回 {id},不会回完整 VO)
|
|
|
|
|
+ applyOptimistic(payload)
|
|
|
|
|
+ // 再调一次 profile 拉服务端权威数据,覆盖包括银行卡默认卡等被服务端修正的字段
|
|
|
|
|
+ try {
|
|
|
|
|
+ const freshVo = await refetchMyProfile()
|
|
|
|
|
+ if (freshVo) applyProfileVo(freshVo)
|
|
|
|
|
+ } catch (refetchErr) {
|
|
|
|
|
+ console.warn("[profile] refetch profile failed:", refetchErr?.message || refetchErr)
|
|
|
|
|
+ }
|
|
|
|
|
+ uni.hideLoading()
|
|
|
|
|
+ uni.showToast({ title: toast, icon: "success" })
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ uni.hideLoading()
|
|
|
|
|
+ uni.showToast({ title: error.message || "保存失败", icon: "none" })
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ saving.value = false
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function editRow(row) {
|
|
function editRow(row) {
|
|
|
|
|
+ if (row.editor === "marital") {
|
|
|
|
|
+ return uni.showActionSheet({
|
|
|
|
|
+ itemList: MARITAL_OPTIONS,
|
|
|
|
|
+ success: (r) => {
|
|
|
|
|
+ if (r.tapIndex < 0) return
|
|
|
|
|
+ const picked = MARITAL_OPTIONS[r.tapIndex]
|
|
|
|
|
+ commitChange(
|
|
|
|
|
+ { maritalStatus: picked },
|
|
|
|
|
+ { toast: "已保存" },
|
|
|
|
|
+ )
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ if (row.editor === "phone") {
|
|
|
|
|
+ return uni.showModal({
|
|
|
|
|
+ title: "修改手机号码",
|
|
|
|
|
+ editable: true,
|
|
|
|
|
+ placeholderText: "请输入新的手机号码",
|
|
|
|
|
+ content: currentUser.phone || "",
|
|
|
|
|
+ success: (r) => {
|
|
|
|
|
+ if (!r.confirm || !r.content?.trim()) return
|
|
|
|
|
+ const phone = r.content.trim()
|
|
|
|
|
+ if (!/^1[3-9]\d{9}$/.test(phone)) {
|
|
|
|
|
+ return uni.showToast({ title: "请输入正确的手机号", icon: "none" });
|
|
|
|
|
+ }
|
|
|
|
|
+ commitChange(
|
|
|
|
|
+ { phone },
|
|
|
|
|
+ { toast: "已保存" },
|
|
|
|
|
+ )
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ if (row.editor === "emergency") {
|
|
|
|
|
+ return (async () => {
|
|
|
|
|
+ const r = await openTwoModal({
|
|
|
|
|
+ title: "修改紧急联系人",
|
|
|
|
|
+ fields: [
|
|
|
|
|
+ { key: "name", label: "名称", placeholder: "请输入联系人姓名", maxlength: 30 },
|
|
|
|
|
+ { key: "phone", label: "手机号", placeholder: "请输入 11 位手机号", inputType: "number", maxlength: 11 },
|
|
|
|
|
+ ],
|
|
|
|
|
+ initial: {
|
|
|
|
|
+ name: currentUser.emergencyName || "",
|
|
|
|
|
+ phone: currentUser.emergencyPhone || "",
|
|
|
|
|
+ },
|
|
|
|
|
+ })
|
|
|
|
|
+ if (!r) return
|
|
|
|
|
+ if (!r.name) return uni.showToast({ title: "请填写名称", icon: "none" })
|
|
|
|
|
+ if (!/^1[3-9]\d{9}$/.test(r.phone)) {
|
|
|
|
|
+ return uni.showToast({ title: "请输入正确的手机号", icon: "none" })
|
|
|
|
|
+ }
|
|
|
|
|
+ const relation = currentUser.emergencyRelation || ""
|
|
|
|
|
+ commitChange(
|
|
|
|
|
+ {
|
|
|
|
|
+ secondLinkName: r.name,
|
|
|
|
|
+ secondLinkPhone: r.phone,
|
|
|
|
|
+ secondLinkRelation: relation,
|
|
|
|
|
+ },
|
|
|
|
|
+ { toast: "已保存" },
|
|
|
|
|
+ )
|
|
|
|
|
+ })()
|
|
|
|
|
+ }
|
|
|
uni.showModal({
|
|
uni.showModal({
|
|
|
title: `修改${row.label}`,
|
|
title: `修改${row.label}`,
|
|
|
editable: true,
|
|
editable: true,
|
|
|
placeholderText: `请输入新的${row.label}`,
|
|
placeholderText: `请输入新的${row.label}`,
|
|
|
success: (r) => {
|
|
success: (r) => {
|
|
|
if (r.confirm && r.content?.trim()) {
|
|
if (r.confirm && r.content?.trim()) {
|
|
|
- lastRequest.value = { field: row.label, value: r.content.trim() };
|
|
|
|
|
- saveProfileChange(lastRequest.value);
|
|
|
|
|
- uni.showToast({ title: "已提交HR审核", icon: "success" });
|
|
|
|
|
|
|
+ uni.showToast({ title: "已提交HR审核", icon: "success" })
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// ============ 银行卡:新增 / 编辑 / 删除 / 设为默认 ============
|
|
|
|
|
+
|
|
|
|
|
+function nextCardId() {
|
|
|
|
|
+ return `local_${Date.now().toString(36)}_${Math.floor(Math.random() * 1000)}`
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 仅本地组装 bankCardList,把后端要求的 accountName 兜底为当前用户姓名
|
|
|
|
|
+function buildBankPayload() {
|
|
|
|
|
+ return bankCardList.value.map((c) => ({
|
|
|
|
|
+ cardNumber: String(c.cardNumber || "").trim(),
|
|
|
|
|
+ openingBank: String(c.openingBank || "").trim(),
|
|
|
|
|
+ accountName: String(c.accountName || currentUser.name || "").trim(),
|
|
|
|
|
+ }))
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function addBankCard() {
|
|
|
|
|
+ const r = await openTwoModal({
|
|
|
|
|
+ title: "新增银行卡",
|
|
|
|
|
+ fields: [
|
|
|
|
|
+ { key: "cardNumber", label: "卡号", placeholder: "请输入完整卡号", inputType: "number", maxlength: 24 },
|
|
|
|
|
+ { key: "openingBank", label: "银行", placeholder: "如:中国工商银行", maxlength: 30 },
|
|
|
|
|
+ ],
|
|
|
|
|
+ })
|
|
|
|
|
+ if (!r) return
|
|
|
|
|
+ if (!r.cardNumber) return uni.showToast({ title: "请填写卡号", icon: "none" })
|
|
|
|
|
+ if (!r.openingBank) return uni.showToast({ title: "请填写银行", icon: "none" })
|
|
|
|
|
+ bankCardList.value.push({
|
|
|
|
|
+ id: nextCardId(),
|
|
|
|
|
+ cardNumber: r.cardNumber,
|
|
|
|
|
+ openingBank: r.openingBank,
|
|
|
|
|
+ branch: "",
|
|
|
|
|
+ });
|
|
|
|
|
+ await commitChange(
|
|
|
|
|
+ { bankCardList: buildBankPayload() },
|
|
|
|
|
+ { toast: "已添加" },
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function editBankCard(card) {
|
|
|
|
|
+ const r = await openTwoModal({
|
|
|
|
|
+ title: "编辑银行卡",
|
|
|
|
|
+ fields: [
|
|
|
|
|
+ { key: "cardNumber", label: "卡号", placeholder: "请输入完整卡号", inputType: "number", maxlength: 24 },
|
|
|
|
|
+ { key: "openingBank", label: "银行", placeholder: "如:中国工商银行", maxlength: 30 },
|
|
|
|
|
+ ],
|
|
|
|
|
+ initial: {
|
|
|
|
|
+ cardNumber: card.cardNumber || "",
|
|
|
|
|
+ openingBank: card.openingBank || "",
|
|
|
|
|
+ },
|
|
|
|
|
+ })
|
|
|
|
|
+ if (!r) return
|
|
|
|
|
+ if (!r.cardNumber) return uni.showToast({ title: "请填写卡号", icon: "none" })
|
|
|
|
|
+ if (!r.openingBank) return uni.showToast({ title: "请填写银行", icon: "none" })
|
|
|
|
|
+ card.cardNumber = r.cardNumber
|
|
|
|
|
+ card.openingBank = r.openingBank
|
|
|
|
|
+ await commitChange(
|
|
|
|
|
+ { bankCardList: buildBankPayload() },
|
|
|
|
|
+ { toast: "已修改" },
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function removeBankCard(card) {
|
|
|
|
|
+ uni.showModal({
|
|
|
|
|
+ title: "删除银行卡",
|
|
|
|
|
+ content: `确定删除 ${bankLabel(card)} 吗?`,
|
|
|
|
|
+ confirmColor: "#ee4d4d",
|
|
|
|
|
+ success: async (r) => {
|
|
|
|
|
+ if (!r.confirm) return;
|
|
|
|
|
+ const idx = bankCardList.value.findIndex((c) => c.id === card.id);
|
|
|
|
|
+ if (idx < 0) return;
|
|
|
|
|
+ bankCardList.value.splice(idx, 1);
|
|
|
|
|
+ await commitChange(
|
|
|
|
|
+ { bankCardList: buildBankPayload() },
|
|
|
|
|
+ { toast: "已删除" },
|
|
|
|
|
+ )
|
|
|
|
|
+ },
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
@@ -125,25 +463,152 @@ function editRow(row) {
|
|
|
color: #c4c8ce;
|
|
color: #c4c8ce;
|
|
|
font-size: 36rpx;
|
|
font-size: 36rpx;
|
|
|
}
|
|
}
|
|
|
-.request-tip {
|
|
|
|
|
- padding: 25rpx 28rpx;
|
|
|
|
|
|
|
+.tip {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ margin: 25rpx 0;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ color: #a0a7b0;
|
|
|
|
|
+ font-size: 21rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.group-title {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- gap: 15rpx;
|
|
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+}
|
|
|
|
|
+.add-bank {
|
|
|
|
|
+ color: #1677ff;
|
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.bank-card-list {
|
|
|
|
|
+ padding: 0 24rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.bank-card {
|
|
|
|
|
+ padding: 24rpx 28rpx;
|
|
|
|
|
+ margin-bottom: 18rpx;
|
|
|
}
|
|
}
|
|
|
-.request-tip > text:first-child {
|
|
|
|
|
|
|
+.bank-head {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+}
|
|
|
|
|
+.bank-title {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 12rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.bank-name {
|
|
|
|
|
+ font-size: 29rpx;
|
|
|
font-weight: 600;
|
|
font-weight: 600;
|
|
|
|
|
+ color: #1f2329;
|
|
|
}
|
|
}
|
|
|
-.request-tip > text:nth-child(2) {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- color: #6f7782;
|
|
|
|
|
- text-align: right;
|
|
|
|
|
|
|
+.bank-actions {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 18rpx;
|
|
|
}
|
|
}
|
|
|
-.tip {
|
|
|
|
|
|
|
+.bank-action {
|
|
|
|
|
+ font-size: 25rpx;
|
|
|
|
|
+ color: #1677ff;
|
|
|
|
|
+}
|
|
|
|
|
+.bank-action.danger {
|
|
|
|
|
+ color: #ee4d4d;
|
|
|
|
|
+}
|
|
|
|
|
+.bank-no {
|
|
|
|
|
+ margin-top: 16rpx;
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ letter-spacing: 1rpx;
|
|
|
|
|
+ color: #1f2329;
|
|
|
|
|
+}
|
|
|
|
|
+.bank-branch {
|
|
|
|
|
+ margin-top: 10rpx;
|
|
|
|
|
+ font-size: 22rpx;
|
|
|
|
|
+ color: #8b939f;
|
|
|
|
|
+}
|
|
|
|
|
+.bank-empty {
|
|
|
|
|
+ margin: 0 24rpx;
|
|
|
|
|
+ padding: 48rpx 28rpx;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 16rpx;
|
|
|
|
|
+ color: #8b939f;
|
|
|
|
|
+ font-size: 25rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.bank-empty > text:last-child {
|
|
|
|
|
+ color: #1677ff;
|
|
|
|
|
+}
|
|
|
|
|
+.two-modal-mask {
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ inset: 0;
|
|
|
|
|
+ background: rgba(0, 0, 0, 0.45);
|
|
|
|
|
+ z-index: 99;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+}
|
|
|
|
|
+.two-modal {
|
|
|
|
|
+ width: 620rpx;
|
|
|
|
|
+ max-width: 92vw;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border-radius: 20rpx;
|
|
|
|
|
+ padding: 36rpx 36rpx 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+.two-modal-title {
|
|
|
display: block;
|
|
display: block;
|
|
|
- margin: 25rpx 0;
|
|
|
|
|
text-align: center;
|
|
text-align: center;
|
|
|
- color: #a0a7b0;
|
|
|
|
|
- font-size: 21rpx;
|
|
|
|
|
|
|
+ font-size: 32rpx;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #1f2329;
|
|
|
|
|
+ margin-bottom: 28rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.two-modal-fields {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 20rpx;
|
|
|
|
|
+ padding-bottom: 8rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.two-modal-field {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ background: #f6f7f8;
|
|
|
|
|
+ border-radius: 12rpx;
|
|
|
|
|
+ padding: 0 22rpx;
|
|
|
|
|
+ min-height: 88rpx;
|
|
|
|
|
+}
|
|
|
|
|
+.two-modal-label {
|
|
|
|
|
+ width: 140rpx;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+ font-size: 26rpx;
|
|
|
|
|
+ color: #6f7782;
|
|
|
|
|
+}
|
|
|
|
|
+.two-modal-input {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ font-size: 28rpx;
|
|
|
|
|
+ color: #1f2329;
|
|
|
|
|
+ background: transparent;
|
|
|
|
|
+}
|
|
|
|
|
+.two-modal-actions {
|
|
|
|
|
+ margin: 28rpx -36rpx 0;
|
|
|
|
|
+ border-top: 1rpx solid #edf0f2;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+}
|
|
|
|
|
+.two-modal-btn {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ height: 96rpx;
|
|
|
|
|
+ line-height: 96rpx;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ font-size: 30rpx;
|
|
|
|
|
+ color: #4b5563;
|
|
|
|
|
+ border-right: 1rpx solid #edf0f2;
|
|
|
|
|
+}
|
|
|
|
|
+.two-modal-btn:last-child {
|
|
|
|
|
+ border-right: 0;
|
|
|
|
|
+ color: #1677ff;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+}
|
|
|
|
|
+.two-modal-btn.primary {
|
|
|
|
|
+ color: #1677ff;
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|