oa_app仓库初始化
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<fy-header :title="spec ? spec.title : '申请'" back />
|
||||
<view v-if="spec" class="body">
|
||||
<view class="field">
|
||||
<text class="lab">标题</text>
|
||||
<view class="box"><input v-model="title" /></view>
|
||||
</view>
|
||||
<view v-if="spec.multiInvoice">
|
||||
<view v-for="(inv, i) in invoices" :key="i" class="card inv">
|
||||
<text class="lab">发票 {{ i + 1 }}</text>
|
||||
<picker :range="ticketTypes" @change="(e) => inv.ticketType = ticketTypes[e.detail.value]">
|
||||
<view class="box">{{ inv.ticketType || '选择票种' }}</view>
|
||||
</picker>
|
||||
<view class="box"><input v-model="inv.amount" type="digit" placeholder="金额" /></view>
|
||||
<view class="box"><input v-model="inv.invoiceNo" placeholder="发票号(选填)" /></view>
|
||||
<picker mode="date" @change="(e) => inv.date = e.detail.value">
|
||||
<view class="box">{{ inv.date || '发票日期(选填)' }}</view>
|
||||
</picker>
|
||||
<view class="box"><input v-model="inv.note" placeholder="备注" /></view>
|
||||
<fy-files :files="inv.files" label="发票附件" editable kind="expense" @update:files="(rows) => inv.files = rows" @remove="(idx) => inv.files.splice(idx, 1)" />
|
||||
</view>
|
||||
<text class="link" @click="invoices.push({ ticketType: '', amount: '', invoiceNo: '', date: '', note: '', files: [], proxyPay: false })">+ 添加发票</text>
|
||||
<view class="field">
|
||||
<text class="lab">报销说明</text>
|
||||
<view class="box tall"><textarea v-model="remark" /></view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-for="f in spec.fields || []" :key="f.key" class="field">
|
||||
<text class="lab">{{ f.label }}</text>
|
||||
<picker v-if="f.type === 'select'" :range="optionsOf(f)" @change="(e) => values[f.key] = optionsOf(f)[e.detail.value]">
|
||||
<view class="box">{{ values[f.key] || '请选择' }}</view>
|
||||
</picker>
|
||||
<picker v-else-if="f.type === 'date'" mode="date" @change="(e) => values[f.key] = e.detail.value">
|
||||
<view class="box">{{ values[f.key] || '请选择日期' }}</view>
|
||||
</picker>
|
||||
<picker v-else-if="f.type === 'datetime'" mode="date" @change="(e) => values[f.key] = e.detail.value">
|
||||
<view class="box">{{ values[f.key] || '请选择日期' }}</view>
|
||||
</picker>
|
||||
<view v-else-if="f.type === 'textarea'" class="box tall"><textarea v-model="values[f.key]" :placeholder="f.placeholder" /></view>
|
||||
<view v-else class="box"><input v-model="values[f.key]" :placeholder="f.placeholder" :type="f.type === 'money' || f.type === 'number' ? 'digit' : 'text'" /></view>
|
||||
</view>
|
||||
<fy-files v-if="!spec.multiInvoice" :files="attachments" label="附件" editable :kind="kind" @update:files="(rows) => attachments = rows" @remove="(idx) => attachments.splice(idx, 1)" />
|
||||
<view v-if="!fixed" class="field">
|
||||
<text class="lab">审批人</text>
|
||||
<picker :range="approverNames" @change="pickApprover">
|
||||
<view class="box">{{ approverLabel || '请选择审批人' }}</view>
|
||||
</picker>
|
||||
</view>
|
||||
<view v-else class="field">
|
||||
<text class="lab">审批链</text>
|
||||
<text class="hint">{{ suggestedLabel || '将按固定审批链流转' }}</text>
|
||||
</view>
|
||||
<view class="btn-primary" @click="submit">提交申请</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FyHeader from '../../components/fy-header.vue'
|
||||
import FyFiles from '../../components/fy-files.vue'
|
||||
import { store } from '../../utils/store.js'
|
||||
import { applyKindById, buildApplyPayload, isFixedChain } from '../../utils/apply.js'
|
||||
import { asList, toast } from '../../utils/format.js'
|
||||
import { oaGet } from '../../utils/api.js'
|
||||
|
||||
export default {
|
||||
components: { FyHeader, FyFiles },
|
||||
data() {
|
||||
return {
|
||||
kind: 'leave',
|
||||
title: '',
|
||||
values: {},
|
||||
invoices: [{ ticketType: '', amount: '', invoiceNo: '', date: '', note: '', files: [], proxyPay: false }],
|
||||
attachments: [],
|
||||
remark: '',
|
||||
approvers: [],
|
||||
suggested: '',
|
||||
suggestedName: '',
|
||||
approver: '',
|
||||
ticketTypes: ['机票', '高铁/火车', '出租车', '住宿', '餐饮招待', '办公用品', '业务招待', '过路费/油费', '其他'],
|
||||
peopleOpts: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
spec() { return applyKindById(this.kind) },
|
||||
fixed() { return isFixedChain(this.kind) },
|
||||
approverNames() { return this.approvers.map((a) => a.realName || a.name || a.username) },
|
||||
approverLabel() {
|
||||
const a = this.approvers.find((x) => x.username === this.approver)
|
||||
return a ? (a.realName || a.name || a.username) : ''
|
||||
},
|
||||
suggestedLabel() { return this.suggestedName }
|
||||
},
|
||||
onLoad(q) {
|
||||
this.kind = q.kind || 'leave'
|
||||
const spec = this.spec
|
||||
const me = store.me
|
||||
if (me && spec) this.title = me.name + ' ' + spec.title
|
||||
this.boot()
|
||||
},
|
||||
methods: {
|
||||
optionsOf(f) {
|
||||
if (f.key === 'targetUsername') return this.peopleOpts.map((p) => p.name)
|
||||
return f.options || []
|
||||
},
|
||||
async boot() {
|
||||
try {
|
||||
const preview = await oaGet('/workflow/chain-preview?type=' + encodeURIComponent(this.kind), store.token)
|
||||
this.approvers = asList(preview && preview.approvers)
|
||||
const chain = asList(preview && preview.suggested)
|
||||
if (chain[0]) {
|
||||
this.suggested = chain[0].username || ''
|
||||
this.suggestedName = chain[0].realName || chain[0].name || this.suggested
|
||||
}
|
||||
if (!this.fixed && !this.approver) this.approver = this.suggested || (this.approvers[0] && this.approvers[0].username) || ''
|
||||
} catch (_) {}
|
||||
if (this.kind === 'layoff') {
|
||||
this.peopleOpts = Object.values(store.people)
|
||||
}
|
||||
},
|
||||
pickApprover(e) {
|
||||
const a = this.approvers[e.detail.value]
|
||||
this.approver = a ? a.username : ''
|
||||
},
|
||||
async submit() {
|
||||
if (!this.spec) return
|
||||
if (!store.can('personal', 'apply')) return toast('没有发起申请权限')
|
||||
if (!this.title.trim()) return toast('请填写标题')
|
||||
if (this.spec.multiInvoice) {
|
||||
const missing = this.invoices.some((r) => Number(r.amount) > 0 && !(r.files && r.files.length))
|
||||
if (missing) return toast('请为有金额的发票上传附件')
|
||||
}
|
||||
let layoffName = ''
|
||||
if (this.kind === 'layoff' && this.values.targetUsername) {
|
||||
const p = this.peopleOpts.find((x) => x.name === this.values.targetUsername || x.username === this.values.targetUsername)
|
||||
if (p) {
|
||||
this.values.targetUsername = p.username
|
||||
layoffName = p.name
|
||||
}
|
||||
}
|
||||
const body = buildApplyPayload({
|
||||
spec: this.spec,
|
||||
title: this.title,
|
||||
values: this.values,
|
||||
invoices: this.invoices,
|
||||
expenseRemark: this.remark,
|
||||
approverUsername: this.approver,
|
||||
suggestedUsername: this.suggested,
|
||||
me: store.me,
|
||||
layoffTargetName: layoffName,
|
||||
attachments: this.attachments
|
||||
})
|
||||
try {
|
||||
await store.submitApply(body)
|
||||
toast('已提交', 'success')
|
||||
setTimeout(() => uni.navigateBack(), 600)
|
||||
} catch (e) {
|
||||
toast(e.message || '提交失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.body { padding: 24rpx; padding-bottom: 80rpx; }
|
||||
.field { margin-bottom: 20rpx; }
|
||||
.lab { font-size: 24rpx; color: #434655; display: block; margin-bottom: 8rpx; }
|
||||
.box { min-height: 88rpx; background: #fff; border-radius: 16rpx; padding: 20rpx 24rpx; font-size: 28rpx; }
|
||||
.box.tall { min-height: 160rpx; }
|
||||
.box textarea, .box input { width: 100%; }
|
||||
.inv { padding: 20rpx; margin-bottom: 16rpx; }
|
||||
.inv .box { margin-top: 12rpx; }
|
||||
.link { color: #2563eb; font-size: 26rpx; display: block; margin-bottom: 20rpx; }
|
||||
.hint { font-size: 24rpx; color: #737686; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user