index.vue 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104
  1. <template>
  2. <div>
  3. <van-nav-bar title="费用报销" left-text="返回" @click-left="back" fixed left-arrow style="z-index:1000" />
  4. <div class="content">
  5. <van-tabs v-model="active" @change="activeChange">
  6. <van-tab title="费用报销" :name="0"></van-tab>
  7. <van-tab title="单据列表" :name="1"></van-tab>
  8. <van-tab title="单据审核" :name="2"></van-tab>
  9. </van-tabs>
  10. <!-- #region 费用报销 -->
  11. <div class="edit" v-if="active == 0">
  12. <van-form class="edit_form" label-width="140">
  13. <!-- 报销人 -->
  14. <van-field label="报销人" @click="ownerIdShowCli(1)" readonly clickable required>
  15. <template #input>
  16. <span v-if="user.userNameNeedTranslate == 1 && formshowText.name"><ww-open-data type='userName'
  17. :openid='formshowText.name'></ww-open-data></span>
  18. <span v-else>{{ formshowText.name }}</span>
  19. </template>
  20. </van-field>
  21. <div v-if="auditTypeItem.auditType == 2">
  22. <van-field label="第一审核人" @click="ownerIdShowCli(2)" readonly clickable required>
  23. <template #input>
  24. <span v-if="user.userNameNeedTranslate == 1"><ww-open-data type='userName'
  25. :openid='firstCheckerText.name'></ww-open-data></span>
  26. <span v-else>{{ firstCheckerText.name }}</span>
  27. </template>
  28. </van-field>
  29. <van-field label="第二审核人" @click="ownerIdShowCli(3)" readonly clickable required>
  30. <template #input>
  31. <span v-if="user.userNameNeedTranslate == 1"><ww-open-data type='userName'
  32. :openid='secondCheckerText.name'></ww-open-data></span>
  33. <span v-else>{{ secondCheckerText.name }}</span>
  34. </template>
  35. </van-field>
  36. </div>
  37. <van-popup v-model="ownerIdShow" position="bottom" v-if="canExamine" style="height: 90%">
  38. <div class="popupDiv paddingTop">
  39. <div class="popupCon conBorder">
  40. <van-radio-group v-model="userRadio" v-if="user.userNameNeedTranslate == '1'">
  41. <div v-for="item in userList" :key="item.id">
  42. <van-radio :name="item" class="popupItem marginNone borderNone" v-if="item.id != user.id || ownerIdShowType == 1">
  43. <!-- <ww-open-data type='userName' :openid='item.name'></ww-open-data> -->
  44. <span class="userNameClass_left">
  45. <ww-open-data type='userName' :openid='item.name'></ww-open-data>
  46. </span>
  47. <span class="userNameClass_right">
  48. {{ item.jobNumber }}
  49. </span>
  50. </van-radio>
  51. </div>
  52. </van-radio-group>
  53. <van-radio-group v-model="userRadio" v-else>
  54. <div v-for="item in userList" :key="item.id">
  55. <van-radio :name="item" class="popupItem marginNone borderNone" v-if="item.id != user.id || ownerIdShowType == 1">
  56. <!-- {{ item.name }} -->
  57. <span class="userNameClass_left">{{item.name}}</span>
  58. <span class="userNameClass_right">{{ item.jobNumber }}</span>
  59. </van-radio>
  60. </div>
  61. </van-radio-group>
  62. </div>
  63. <div class="popupBtn">
  64. <van-button style="width:100%;background: #1989fa;color: #ffffff;" round
  65. @click="ownerIdChange()">确定</van-button>
  66. </div>
  67. </div>
  68. </van-popup>
  69. <!-- 填报日期 -->
  70. <van-field v-model="editForm.createDate" label="填报日期" @click="createDateShow = true" readonly clickable
  71. required></van-field>
  72. <van-popup v-model="createDateShow" position="bottom">
  73. <van-datetime-picker type="date" title="选择填报日期" @confirm="createDateChange" v-model="currentDate1"
  74. @cancel="createDateShow = false; $forceUpdate();" :min-date="minDate" :max-date="maxDate" />
  75. </van-popup>
  76. <!-- 发票张数 -->
  77. <van-field label="发票张数" v-if="user.timeType.easyExpense==0">
  78. <template #input>
  79. <van-stepper v-model="editForm.ticketNum" disable-input @plus="ticNumChange(1)"
  80. @minus="ticNumChange(0)" />
  81. </template>
  82. </van-field>
  83. <!-- 费用类型 -->
  84. <van-field v-model="editForm.type" label="费用主类型" @click="typeShow = true" readonly clickable>
  85. <template #input>{{ expenseMainType.text }}</template>
  86. </van-field>
  87. <van-popup v-model="typeShow" position="bottom">
  88. <van-picker show-toolbar :columns="typeList" @confirm="typeChange"
  89. @cancel="typeShow = false; $forceUpdate();" />
  90. </van-popup>
  91. <!-- 备注 -->
  92. <van-field v-model="editForm.remark" label="备注" type="textarea" maxlength="100"></van-field>
  93. <!-- 发票 -->
  94. <van-field label="发票" readonly>
  95. <template #input>总费用: ¥{{ totalCost }}</template>
  96. </van-field>
  97. <div class="invoice" v-if="invoiceList.length != 0">
  98. <div v-for="item, index in invoiceList" :key="item.id" style="position:relative"
  99. :class="index == 0 ? '' : 'invoice_item'">
  100. <!-- <van-button class="deletebtn" size="mini" type="default" @click="deleteInvoice(index)">删除</van-button> -->
  101. <van-icon name="delete-o" class="deletebtn" v-if="index != 0" @click="deleteInvoice(index)" />
  102. <van-field label="所属项目:" v-model="item.projectId"
  103. @click="in_projectShow = true, invoiceIndex = index" readonly clickable required>
  104. <template #input>{{ formshowText.inProjectName[index] }}</template>
  105. </van-field>
  106. <van-field label="费用日期:" v-model="item.happenDate"
  107. @click="in_dateShow = true, invoiceIndex = index" readonly clickable required></van-field>
  108. <van-field label="发票种类:" v-model="item.invoiceType" v-if="user.timeType.easyExpense==0"
  109. @click="in_typeShow = true, invoiceIndex = index" readonly clickable required>
  110. <template #input>{{ inTypeList[item.invoiceType] }}</template>
  111. </van-field>
  112. <van-field label="费用类型:" v-model="item.expenseType"
  113. @click="in_exTypeShow = true, invoiceIndex = index" readonly clickable required></van-field>
  114. <van-field :label="`${user.timeType.easyExpense==0?'费用金额(含税)':'费用金额'}:`" v-model="item.amount" type="number" required></van-field>
  115. <van-field label="发票号:" v-model="item.invoiceNo" v-if="user.timeType.easyExpense==0"></van-field>
  116. <van-field label="税率%:" v-model="item.taxPercent" v-if="user.timeType.easyExpense==0"></van-field>
  117. <van-field label="税额:" readonly v-if="user.timeType.easyExpense==0">
  118. <template #input>¥{{ getTaxValue(item.amount, item.taxPercent) }}</template>
  119. </van-field>
  120. <van-field label="备注:" v-model="item.remark" autosize maxlength="100"></van-field>
  121. <!-- <van-field label="报销凭证:" @click="invoiceIndex = index" clickable>
  122. <template #input>
  123. <van-uploader v-model="uploader[index]" :before-read="beforeRead"
  124. :after-read="afterRead" @delete="item.pic = null" :max-count="1" />
  125. </template>
  126. </van-field> -->
  127. <van-field @click="invoiceIndex = index" clickable label-width="0em">
  128. <template #input>
  129. <div class="imgListVan">
  130. <div>报销凭证:</div>
  131. <div class="vanUploaderImg">
  132. <van-uploader v-model="uploader[index]" :before-read="beforeRead" multiple
  133. :after-read="afterRead" @delete="(file, Item) => {deteleImg(file, Item, index)}" :max-count="10" />
  134. </div>
  135. </div>
  136. </template>
  137. </van-field>
  138. </div>
  139. </div>
  140. <div class="addinvoice"><van-button size="small" icon="plus" type="info" plain hairline
  141. @click="addInvoice">添加发票</van-button></div>
  142. <!-- 发票-popup -->
  143. <span>
  144. <!-- 所属项目 -->
  145. <!-- <van-popup v-model="in_projectShow" position="bottom">
  146. <van-picker value-key="projectName" show-toolbar :columns="inProjectList"
  147. @confirm="inProjectChange" @cancel="in_projectShow = false; $forceUpdate();" />
  148. </van-popup> -->
  149. <van-popup v-model="in_projectShow" position="bottom" style="height: 84%">
  150. <div class="popupDiv">
  151. <div class="popupSearch">
  152. <van-search v-model.trim="projectSelectVal" shape="round" background="#F4F4F4" placeholder="请输入项目名称/编号" @clear="projectSelect()" @blur="projectSelect()" @search="projectSelect()" @input="projectSelect()"/>
  153. </div>
  154. <div class="popupCon">
  155. <div v-for="(item, index) in inProjectList" :key="item.id" class="popupItem paddingDiv" @click="inProjectChange(item, index)">
  156. <p class="popupItemOne" v-if="item.projectName">{{item.projectName}}</p>
  157. <p class="popupItemTwo" v-if="item.projectCode">{{item.projectCode}}</p>
  158. </div>
  159. </div>
  160. </div>
  161. </van-popup>
  162. <!-- 费用日期 -->
  163. <van-popup v-model="in_dateShow" position="bottom">
  164. <van-datetime-picker type="date" title="选择费用日期" @confirm="inDateChange" v-model="currentDate2"
  165. @cancel="in_dateShow = false; $forceUpdate();" :min-date="minDate" :max-date="maxDate" />
  166. </van-popup>
  167. <!-- 发票种类 -->
  168. <van-popup v-model="in_typeShow" position="bottom">
  169. <van-picker show-toolbar :columns="inTypeList" @confirm="inTypeChange"
  170. @cancel="in_typeShow = false; $forceUpdate();" />
  171. </van-popup>
  172. <!-- 费用类型 -->
  173. <van-popup v-model="in_exTypeShow" position="bottom">
  174. <van-picker value-key="typeName" show-toolbar :columns="inexTypeList" @confirm="inexTypeChange"
  175. @cancel="in_exTypeShow = false; $forceUpdate();" />
  176. </van-popup>
  177. </span>
  178. </van-form>
  179. <!-- 提交 -->
  180. <div class="form_btn" style="position:fixed; bottom:0px;width:100%;">
  181. <div style="padding-bottom:10px;">
  182. <van-button square block type="info" @click="submitExpense" :loading="confirmLoading"
  183. style="width:100%;float:left;">提交</van-button>
  184. </div>
  185. </div>
  186. </div>
  187. <!-- #endregion -->
  188. <!-- 单据列表 -->
  189. <div class="list" v-if="active == 1">
  190. <!-- <van-pull-refresh v-model="downLoading" @refresh="onDownRefresh">
  191. <van-list v-model="uploading" :finished="upFinished" :immediate-check="false" :offset="100" finished-text="没有更多了" @load="onLoadList"></van-list>
  192. </van-pull-refresh> -->
  193. <van-collapse v-model="activeName" accordion class="list_collapse">
  194. <van-collapse-item v-for="item in billList" :key="item.id" title="标题1" :name="item.id">
  195. <template #title>
  196. <div class="collapse_label_l">票据编号:{{ item.code }}</div>
  197. <div class="collapse_label_r">报销人:
  198. <span v-if="user.userNameNeedTranslate == 1"><ww-open-data type='userName'
  199. :openid='item.ownerName'></ww-open-data></span>
  200. <span v-else>{{ item.ownerName }}</span>
  201. </div>
  202. <div class="collapse_label_l">金额: ¥{{ item.totalAmount | numtosum }}</div>
  203. <div class="collapse_label_r">状态:<span :class="statusClass[item.status]">{{
  204. statusList[item.status] }}</span></div>
  205. </template>
  206. <div class="wrapper">
  207. <div><span>票据编号:</span><span>{{ item.code }}</span></div>
  208. <div><span>金额:</span><span>¥{{ item.totalAmount | numtosum }}</span></div>
  209. <div><span>报销人:</span>
  210. <span v-if="user.userNameNeedTranslate == 1"><ww-open-data type='userName'
  211. :openid='item.ownerName'></ww-open-data></span>
  212. <span v-else>{{ item.ownerName }}</span>
  213. </div>
  214. <div><span>填报日期:</span><span>{{ item.createDate }}</span></div>
  215. <div v-if="user.timeType.easyExpense==0"><span>发票张数:</span><span>{{ item.ticketNum }}</span></div>
  216. <div><span>费用类型:</span><span>{{ item.expenseMainTypeName }}</span></div>
  217. <!-- <div><span>状态:</span><span>{{item.status}}</span></div> -->
  218. <!-- <div><span>驳回原因:</span><span>{{item.denyReason}}</span></div> -->
  219. <div><span>备注:</span><span>{{ item.remark }}</span></div>
  220. </div>
  221. <div class="operation">
  222. <van-button size="small" type="info"
  223. :to="{ name: 'expenseDetails', params: { id: item.id, canEdit: false } }">查看</van-button>
  224. <van-button style="margin-left:10px" size="small" type="info"
  225. :to="{ name: 'expenseDetails', params: { id: item.id, canEdit: true } }">编辑</van-button>
  226. <van-button style="margin-left:10px" size="small" type="danger"
  227. @click="deleteBill(item.id)">删除</van-button>
  228. </div>
  229. </van-collapse-item>
  230. </van-collapse>
  231. </div>
  232. <!-- 单据审核 -->
  233. <div class="audit" v-if="active == 2">
  234. <van-collapse v-model="auditName" accordion class="list_collapse">
  235. <van-collapse-item v-for="item in examineList" :key="item.id" title="标题2" :name="item.id">
  236. <template #title>
  237. <div class="collapse_label_l">票据编号:{{ item.code }}</div>
  238. <div class="collapse_label_r">报销人:
  239. <span v-if="user.userNameNeedTranslate == 1"><ww-open-data type='userName'
  240. :openid='item.ownerName'></ww-open-data></span>
  241. <span v-else>{{ item.ownerName }}</span>
  242. </div>
  243. <div class="collapse_label_l">金额: ¥{{ item.totalAmount | numtosum }}</div>
  244. <div class="collapse_label_r">状态:<span :class="statusClass[item.status]">{{
  245. statusList[item.status] }}</span></div>
  246. <div class="operation" v-if="auditTypeItem.auditType == 0">
  247. <van-button size="small" type="info" :loading="item.approveLoading"
  248. @click.stop="approve(item)">通过</van-button>
  249. <van-button style="margin-left:15px" size="small" type="danger"
  250. @click.stop="denyToReason(item.id)">驳回</van-button>
  251. </div>
  252. <div class="operation" v-if="auditTypeItem.auditType == 1">
  253. <van-button size="small" type="info"
  254. :to="{ name: 'expenseDetails', params: { id: item.id, canEdit: false, isAudit:true } }">审核</van-button>
  255. </div>
  256. <div class="operation" v-if="auditTypeItem.auditType == 2">
  257. <van-button size="small" type="info" :loading="item.approveLoading"
  258. @click.stop="approve(item)">通过</van-button>
  259. <van-button style="margin-left:15px" size="small" type="danger"
  260. @click.stop="denyToReason(item.id)">驳回</van-button>
  261. </div>
  262. </template>
  263. <div class="wrapper">
  264. <div><span>票据编号:</span><span>{{ item.code }}</span></div>
  265. <div><span>金额:</span><span>¥{{ item.totalAmount | numtosum }}</span></div>
  266. <div><span>报销人:</span>
  267. <span v-if="user.userNameNeedTranslate == 1"><ww-open-data type='userName'
  268. :openid='item.ownerName'></ww-open-data></span>
  269. <span v-else>{{ item.ownerName }}</span>
  270. </div>
  271. <div><span>填报日期:</span><span>{{ item.createDate }}</span></div>
  272. <div v-if="user.timeType.easyExpense==0"><span>发票张数:</span><span>{{ item.ticketNum }}</span></div>
  273. <div><span>费用类型:</span><span>{{ item.expenseMainTypeName }}</span></div>
  274. <div>
  275. <span>审核流程:</span>
  276. <span v-if="item.reviewProcess == 0">待第一审核人审核</span>
  277. <span v-if="item.reviewProcess == 1">待第二审核人审核</span>
  278. <span v-if="item.reviewProcess == 2">审核完成</span>
  279. </div>
  280. <!-- <div><span>状态:</span><span>{{item.status}}</span></div> -->
  281. <!-- <div><span>驳回原因:</span><span>{{item.denyReason}}</span></div> -->
  282. <div><span>备注:</span><span>{{ item.remark }}</span></div>
  283. </div>
  284. <div class="lookup">
  285. <van-button size="small" type="info"
  286. :to="{ name: 'expenseDetails', params: { id: item.id, canEdit: false } }">查看单据信息</van-button>
  287. </div>
  288. </van-collapse-item>
  289. </van-collapse>
  290. <!-- <van-popup v-model="denyReasonDialog" position="bottom" closeable >
  291. <van-cell>请输入原因</van-cell>
  292. <van-field class="form_input"
  293. v-model="denyParm.denyReason" name="reason" type="textarea" placeholder="请输入您决定驳回的原因"
  294. rows="3" autosize />
  295. <van-button style="width:100%;" type="info" :loading="denyLoading" @click="deny()">提交</van-button>
  296. </van-popup> -->
  297. </div>
  298. </div>
  299. </div>
  300. </template>
  301. <script>
  302. export default {
  303. data() {
  304. return {
  305. auditTypeItem: {auditType: 0},
  306. active: sessionStorage.page ? JSON.parse(sessionStorage.page) : 0,
  307. user: JSON.parse(localStorage.userInfo),
  308. userList: [],
  309. canExamine: false,
  310. currentDate1: new Date(),
  311. currentDate2: new Date(),
  312. minDate: new Date(2020, 0, 1),
  313. maxDate: new Date(2025, 11, 31),
  314. confirmLoading: false,
  315. denyLoading: false,
  316. formshowText: {
  317. name: '',
  318. inProjectName: []
  319. },
  320. // #region 费用报销
  321. editForm: {
  322. ownerId: '',
  323. createDate: this.formatDate(new Date()),
  324. ticketNum: 0,
  325. type: 0,
  326. remark: ''
  327. },
  328. userRadio: null,
  329. ownerIdShow: false,
  330. ownerIdShowType: 1, // 1,报销人, 2第一审核人, 3第二审核人
  331. createDateShow: false,
  332. typeShow: false,
  333. typeList: [],
  334. invoiceIndex: 0,
  335. invoiceList: [
  336. {
  337. projectId: '',
  338. happenDate: '',
  339. invoiceType: '',
  340. expenseType: '',
  341. amount: '',
  342. invoiceNo: '',
  343. taxPercent: '',
  344. taxValue: '',
  345. remark: '',
  346. pic: '',
  347. }
  348. ],
  349. in_projectShow: false,
  350. in_dateShow: false,
  351. in_typeShow: false,
  352. in_exTypeShow: false,
  353. inProjectList: [],
  354. inProjectListCopy: [],
  355. inTypeList: ['增值税专用发票', '增值税普通发票'],
  356. allexTypeList: [],
  357. inexTypeList: [],
  358. uploader: [[]],
  359. // #endregion
  360. // 单据列表
  361. activeName: '',
  362. billList: [],
  363. statusList: ['审核通过', '待审核', '已驳回', '已撤销'],
  364. statusClass: ['', 'waiting', 'rejected', ''],
  365. uploading: false,
  366. // 单据审核
  367. auditName: '',
  368. examineList: [],
  369. denyReasonDialog: false,
  370. denyParm: {
  371. id: '',
  372. denyReason: ''
  373. },
  374. expenseMainType: {
  375. text: ''
  376. },
  377. projectSelectVal: '',
  378. firstCheckerText: {},
  379. secondCheckerText: {}
  380. }
  381. },
  382. computed: {
  383. totalCost() {
  384. let costnum = 0
  385. for (let i in this.invoiceList) {
  386. costnum += (this.invoiceList[i].amount ? parseFloat(this.invoiceList[i].amount) : 0)
  387. }
  388. return costnum.toFixed(2)
  389. }
  390. },
  391. filters: {
  392. numtosum(value) {
  393. if (value == undefined || !value) return '0.00'
  394. value = value.toFixed(2)
  395. const intPart = Math.trunc(value)
  396. const intPartFormat = intPart.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
  397. let floatPart = '.00'
  398. const valueArray = value.toString().split('.')
  399. if (valueArray.length === 2) { // 有小数部分
  400. floatPart = valueArray[1].toString() // 取得小数部分
  401. return intPartFormat + '.' + floatPart
  402. }
  403. return intPartFormat + floatPart
  404. },
  405. },
  406. mounted() {
  407. this.formshowText.name = this.user.name;
  408. this.editForm.ownerId = this.user.id;
  409. this.userRadio = {
  410. name: this.user.name,
  411. id: this.user.id
  412. }
  413. this.activeChange()
  414. for (let i in this.user.functionList) {
  415. if (this.user.functionList[i].name == '费用审核') {
  416. this.canExamine = true
  417. }
  418. }
  419. if (!this.canExamine) {
  420. this.editForm.ownerId = this.user.id
  421. this.formshowText.name = this.user.name
  422. }
  423. this.getUserList()
  424. this.getProjectList()
  425. this.getExpensMainTypes()
  426. this.getExTypeList()
  427. this.getAuditType();
  428. },
  429. methods: {
  430. // 项目搜索
  431. projectSelect() {
  432. if(this.projectSelectVal.length > 0) {
  433. let data = this.inProjectListCopy.filter(item => {return item.projectName && item.projectName.includes(this.projectSelectVal)});
  434. let dataList = this.inProjectListCopy.filter(item => {return item.projectCode && item.projectCode.includes(this.projectSelectVal)});
  435. let dataTree = data.concat(dataList)
  436. let arrList = Array.from(new Set(dataTree))
  437. this.inProjectList = arrList
  438. } else {
  439. this.inProjectList = JSON.parse(JSON.stringify(this.inProjectListCopy))
  440. }
  441. },
  442. back() {
  443. sessionStorage.removeItem("page");
  444. history.back();
  445. },
  446. formatDate(date) {
  447. let mon = date.getMonth() + 1
  448. return `${date.getFullYear()}-${mon < 10 ? '0' + mon : mon}-${date.getDate() < 10 ? '0' + date.getDate() : date.getDate()}`;
  449. },
  450. getTaxValue(amount, percent) {
  451. let per = percent / 100
  452. let amo = amount / (1 + per) * per
  453. return amo.toFixed(2)
  454. },
  455. activeChange() {
  456. sessionStorage.setItem('page', JSON.stringify(this.active))
  457. if (this.active == 1) {
  458. this.getBillList()
  459. }
  460. if (this.active == 2) {
  461. this.getExamineList()
  462. }
  463. },
  464. // #region 费用报销
  465. ownerIdChange() {
  466. if(this.ownerIdShowType == 1) {
  467. this.editForm.ownerId = this.userRadio.id
  468. this.formshowText.name = this.userRadio.name
  469. } else if(this.ownerIdShowType == 2) {
  470. this.$set(this.firstCheckerText, 'id', this.userRadio.id)
  471. this.$set(this.firstCheckerText, 'name', this.userRadio.name)
  472. } else if(this.ownerIdShowType == 3) {
  473. this.$set(this.secondCheckerText, 'id', this.userRadio.id)
  474. this.$set(this.secondCheckerText, 'name', this.userRadio.name)
  475. }
  476. this.ownerIdShow = false
  477. },
  478. createDateChange(value, key) {
  479. this.editForm.createDate = this.formatDate(value)
  480. this.createDateShow = false
  481. },
  482. typeChange(value, key) {
  483. this.editForm.type = value.id
  484. this.expenseMainType.text = value.name
  485. this.typeShow = false
  486. for (let i in this.invoiceList) {
  487. this.invoiceList[i].expenseType = ''
  488. }
  489. this.inexTypeList = this.allexTypeList.filter(a => a.mainType == this.editForm.type)
  490. },
  491. ticNumChange(value) {
  492. if (value) {
  493. this.invoiceList.push({
  494. projectId: '',
  495. happenDate: '',
  496. invoiceType: '',
  497. expenseType: '',
  498. amount: '',
  499. invoiceNo: '',
  500. taxPercent: '',
  501. taxValue: '',
  502. remark: '',
  503. pic: '',
  504. })
  505. } else {
  506. this.invoiceList.pop()
  507. }
  508. },
  509. // 发票
  510. inProjectChange(value, key) {
  511. this.formshowText.inProjectName[this.invoiceIndex] = value.projectName
  512. this.invoiceList[this.invoiceIndex].projectId = value.id
  513. this.in_projectShow = false
  514. },
  515. inDateChange(value, key) {
  516. this.invoiceList[this.invoiceIndex].happenDate = this.formatDate(value)
  517. this.in_dateShow = false
  518. },
  519. inTypeChange(value, key) {
  520. this.invoiceList[this.invoiceIndex].invoiceType = key
  521. this.in_typeShow = false
  522. },
  523. inexTypeChange(value, key) {
  524. this.invoiceList[this.invoiceIndex].expenseType = value.typeName
  525. this.in_exTypeShow = false
  526. },
  527. addInvoice() {
  528. this.invoiceList.push({
  529. projectId: '',
  530. happenDate: '',
  531. invoiceType: '',
  532. expenseType: '',
  533. amount: '',
  534. invoiceNo: '',
  535. taxPercent: '',
  536. taxValue: '',
  537. remark: '',
  538. pic: '',
  539. })
  540. this.uploader.push([])
  541. this.editForm.ticketNum = this.invoiceList.length
  542. },
  543. deleteInvoice(index) {
  544. this.invoiceList.splice(index, 1)
  545. this.uploader.splice(index, 1)
  546. this.editForm.ticketNum = this.invoiceList.length
  547. },
  548. // 上传报销凭证
  549. beforeRead(file) {
  550. // console.log(file, file.length)
  551. // console.log(Array.isArray(file))
  552. // if (file.type != 'image/jpeg' && file.type != 'image/png' && file.type != 'image/jpg') {
  553. // this.$toast.fail('请选择jpg或png格式的图片')
  554. // return false
  555. // }
  556. // return true
  557. let newFile = Array.isArray(file) ? file : [file]
  558. for(var i in newFile) {
  559. const fileItem = newFile[i]
  560. if (fileItem.type != 'image/jpeg' && fileItem.type != 'image/png' && fileItem.type != 'image/jpg') {
  561. this.$toast.fail('请选择jpg或png格式的图片')
  562. return false
  563. }
  564. }
  565. return true
  566. },
  567. afterRead(file) {
  568. // let formData = new FormData();
  569. // formData.append("multipartFile", file.file);
  570. // this.$axios.post("/common/uploadFile", formData)
  571. // .then(res => {
  572. // if (res.code == "ok") {
  573. // this.invoiceList[this.invoiceIndex].pic = res.data
  574. // } else {
  575. // this.$toast.fail('上传失败');
  576. // this.uploader[this.invoiceIndex] = []
  577. // }
  578. // }).catch(err => { this.$toast.clear(); console.log(err); this.uploader[this.invoiceIndex] = [] });
  579. let newFile = Array.isArray(file) ? file : [file]
  580. let formData = new FormData();
  581. for(var i in newFile) {
  582. formData.append("multipartFiles", newFile[i].file);
  583. }
  584. this.$axios.post("/common/uploadFileArray", formData)
  585. .then(res => {
  586. if (res.code == "ok") {
  587. let newPic = this.invoiceList[this.invoiceIndex].pic || []
  588. let newData = res.data.split(';')
  589. this.invoiceList[this.invoiceIndex].pic = [...newPic, ...newData]
  590. console.log(this.invoiceList[this.invoiceIndex].pic, '<=== 合计图片')
  591. } else {
  592. this.$toast.fail('上传失败');
  593. this.uploader[this.invoiceIndex] = []
  594. }
  595. }).catch(err => { this.$toast.clear(); console.log(err); this.uploader[this.invoiceIndex] = [] });
  596. },
  597. // 删除图片
  598. deteleImg(file, item, arrIndex) {
  599. let index = item.index
  600. this.invoiceList[arrIndex].pic = this.invoiceList[arrIndex].pic.filter((item,i) => i != index)
  601. },
  602. // 提交
  603. submitExpense() {
  604. if (!this.editForm.ownerId) {
  605. this.$toast.fail('请选择报销人')
  606. return
  607. }
  608. if (!this.editForm.createDate) {
  609. this.$toast.fail('请选择填报日期')
  610. return
  611. }
  612. if(this.auditTypeItem.auditType == 2) {
  613. if(!this.firstCheckerText.id || !this.secondCheckerText.id) {
  614. this.$toast.fail(`${!this.firstCheckerText.id ? '请选择第一审核人' : '请选择第二审核人'}`)
  615. return
  616. } else {
  617. this.editForm.firstCheckerId = this.firstCheckerText.id
  618. this.editForm.secondCheckerId = this.secondCheckerText.id
  619. }
  620. }
  621. let required1 = false
  622. let required2 = false
  623. let required3 = false
  624. let required4 = false
  625. let required5 = false
  626. for (let i in this.invoiceList) {
  627. if (!this.invoiceList[i].projectId) {
  628. required1 = '所属项目'
  629. }
  630. if (!this.invoiceList[i].happenDate) {
  631. required2 = '费用日期'
  632. }
  633. // if (!this.invoiceList[i].invoiceType) {
  634. // required3 = '发票种类'
  635. // }
  636. if (!this.invoiceList[i].expenseType) {
  637. required4 = '费用类型'
  638. }
  639. if (!this.invoiceList[i].amount) {
  640. required5 = this.user.timeType.easyExpense==0?'费用金额(含税)':'费用金额'
  641. }
  642. }
  643. if (required1 || required2 || required3 || required4 || required5) {
  644. let requiredStr = (required1 ? required1 + '、' : '')
  645. + (required2 ? required2 + '、' : '')
  646. + (required3 ? required3 + '、' : '')
  647. + (required4 ? required4 + '、' : '')
  648. + (required5 ? required5 + '、' : '')
  649. requiredStr = requiredStr.substring(0, requiredStr.length - 1)
  650. this.$toast.fail('请添加发票的[' + requiredStr + ']')
  651. return
  652. }
  653. if (this.invoiceList.length == 0) {
  654. this.$toast.fail('请添加发票')
  655. return
  656. }
  657. for (let i in this.invoiceList) {
  658. this.invoiceList[i].taxValue = this.getTaxValue(this.invoiceList[i].amount, this.invoiceList[i].taxPercent)
  659. }
  660. let newList = JSON.parse(JSON.stringify(this.invoiceList))
  661. for(let i in newList) {
  662. if(newList[i].pic) {
  663. newList[i].pic = newList[i].pic.join(',')
  664. }
  665. }
  666. // this.editForm.items = JSON.stringify(this.invoiceList)
  667. this.editForm.items = JSON.stringify(newList)
  668. this.editForm.totalAmount = this.totalCost
  669. // 获取新的票据编号
  670. this.confirmLoading = true
  671. this.$axios.post("/expense-sheet/getNextCode", {})
  672. .then(res => {
  673. if (res.code == "ok") {
  674. this.editForm.code = res.data
  675. // 提交
  676. this.$axios.post("/expense-sheet/add", this.editForm)
  677. .then(res => {
  678. this.confirmLoading = false;
  679. if (res.code == "ok") {
  680. this.$toast.success('填报成功')
  681. this.editForm = {
  682. ownerId: '',
  683. createDate: this.formatDate(new Date()),
  684. ticketNum: 0,
  685. type: 0,
  686. remark: ''
  687. }
  688. // this.formshowText = {
  689. // name: '',
  690. // inProjectName: []
  691. // }
  692. this.$set(this, 'firstCheckerText', {})
  693. this.$set(this, 'secondCheckerText', {})
  694. this.formshowText.name = ''
  695. this.formshowText.inProjectName = []
  696. this.expenseMainType.text=''
  697. this.invoiceList = []
  698. this.uploader = []
  699. this.formshowText.name = this.user.name;
  700. this.editForm.ownerId = this.user.id;
  701. this.userRadio = {
  702. name: this.user.name,
  703. id: this.user.id
  704. }
  705. } else {
  706. this.$toast.fail(res.msg || res.code || '填报失败');
  707. }
  708. }).catch(err => { this.confirmLoading = false; this.$toast.clear(); console.log(err) });
  709. } else {
  710. this.confirmLoading = false
  711. this.$toast.fail('获取失败');
  712. }
  713. }).catch(err => {
  714. this.confirmLoading = false;
  715. this.$toast.clear();
  716. console.log(err)
  717. });
  718. },
  719. // #endregion
  720. // 单据列表
  721. deleteBill(pid) {
  722. this.$dialog.confirm({
  723. message: '确认删除?',
  724. })
  725. .then(() => {
  726. // on confirm
  727. this.$axios.post("/expense-sheet/delete", { id: pid })
  728. .then(res => {
  729. if (res.code == "ok") {
  730. this.$toast.success('删除成功')
  731. this.getBillList()
  732. } else {
  733. this.$toast.fail(res.msg || res.code || '删除失败');
  734. }
  735. }).catch(err => { this.$toast.clear(); console.log(err) });
  736. })
  737. .catch(() => {
  738. // on cancel
  739. });
  740. },
  741. getAuditType() {
  742. this.$axios.post("/expense-audit-setting/get", {})
  743. .then(res => {
  744. if (res.code == "ok") {
  745. this.auditTypeItem = res.data;
  746. } else {
  747. this.$message({
  748. message: res.msg,
  749. type: "error"
  750. });
  751. }
  752. }).catch(err => { this.$toast.clear(); console.log(err) });
  753. },
  754. // 单据审核
  755. approve(item) {
  756. item.approveLoading = true
  757. this.$axios.post("/expense-sheet/approve", { id: item.id })
  758. .then(res => {
  759. if (res.code == "ok") {
  760. this.$toast.success('已通过')
  761. item.approveLoading = false
  762. this.getExamineList()
  763. } else {
  764. this.$toast.fail('操作失败');
  765. }
  766. }).catch(err => { this.$toast.clear(); console.log(err) });
  767. },
  768. denyToReason(pid) {
  769. this.denyParm.id = pid
  770. this.denyReasonDialog = true
  771. this.denyParm.denyReason = ''
  772. this.deny()
  773. },
  774. deny() {
  775. this.denyLoading = true
  776. this.$axios.post("/expense-sheet/deny", this.denyParm)
  777. .then(res => {
  778. if (res.code == "ok") {
  779. this.$toast.success('已驳回')
  780. this.denyReasonDialog = false
  781. this.denyLoading = false
  782. this.getExamineList()
  783. } else {
  784. this.$toast.fail(res.msg || res.code || '操作失败');
  785. }
  786. }).catch(err => { this.$toast.clear(); console.log(err) });
  787. },
  788. getUserList() {
  789. this.$axios.post("/user/getSimpleActiveUserList", {})
  790. .then(res => {
  791. if (res.code == "ok") {
  792. this.userList = res.data
  793. } else {
  794. this.$toast.fail(res.msg || res.code || '获取失败');
  795. }
  796. }).catch(err => { this.$toast.clear(); console.log(err) });
  797. },
  798. getProjectList() {
  799. this.$axios.post("/project/getProjectList", {})
  800. .then(res => {
  801. if (res.code == "ok") {
  802. this.inProjectList = res.data
  803. this.inProjectListCopy = JSON.parse(JSON.stringify(res.data))
  804. } else {
  805. this.$toast.fail(res.msg || res.code || '获取失败');
  806. }
  807. }).catch(err => { this.$toast.clear(); console.log(err) });
  808. },
  809. getExTypeList() {
  810. this.$axios.post("/expense-type/getList", {})
  811. .then(res => {
  812. if (res.code == "ok") {
  813. this.allexTypeList = res.data
  814. this.inexTypeList = this.allexTypeList.filter(a => a.mainType == this.editForm.type)
  815. } else {
  816. this.$toast.fail(res.msg || res.code || '获取失败');
  817. }
  818. }).catch(err => { this.$toast.clear(); console.log(err) });
  819. },
  820. getExpensMainTypes() {
  821. this.$axios.post("/expense-main-type/list", {})
  822. .then(res => {
  823. if (res.code == "ok") {
  824. for (var i in res.data) {
  825. res.data[i].text = res.data[i].name
  826. }
  827. this.typeList = res.data
  828. this.expenseMainType.text = res.data[0].name
  829. this.editForm.type = res.data[0].id
  830. this.getExTypeList
  831. } else {
  832. this.$toast.fail(res.msg || res.code || '获取失败');
  833. }
  834. }).catch(err => { this.$toast.clear(); console.log(err) });
  835. },
  836. getBillList() {
  837. this.$axios.post("/expense-sheet/list", {
  838. pageSize: 999,
  839. pageIndex: 1,
  840. startDate: '',
  841. endDate: '',
  842. ownerId: '',
  843. type: ''
  844. }).then(res => {
  845. if (res.code == "ok") {
  846. this.billList = res.data.records
  847. } else {
  848. this.$toast.fail(res.msg || res.code || '获取失败');
  849. }
  850. }).catch(err => { this.$toast.clear(); console.log(err) });
  851. },
  852. getExamineList() {
  853. this.$axios.post("/expense-sheet/list", {
  854. pageSize: 999,
  855. pageIndex: 1,
  856. startDate: '',
  857. endDate: '',
  858. ownerId: '',
  859. type: '',
  860. status: 1
  861. }).then(res => {
  862. if (res.code == "ok") {
  863. this.examineList = res.data.records
  864. for (let i in this.examineList) {
  865. this.$set(this.examineList[i], 'approveLoading', false)
  866. }
  867. } else {
  868. this.$toast.fail(res.msg || res.code || '获取失败');
  869. }
  870. }).catch(err => { this.$toast.clear(); console.log(err) });
  871. },
  872. ownerIdShowCli(type) {
  873. console.log('执行')
  874. this.ownerIdShowType = type
  875. this.userRadio = ''
  876. this.ownerIdShow = true
  877. if(type > 1) {
  878. this.canExamine = true
  879. }
  880. }
  881. },
  882. }
  883. </script>
  884. <style lang="less" scoped>
  885. .content {
  886. margin-top: 46px;
  887. overflow: auto;
  888. .edit {
  889. .userCheckbox {
  890. padding: 10px;
  891. }
  892. padding-bottom: 46px;
  893. .form_btn {
  894. z-index: 1000;
  895. }
  896. .edit_form {
  897. .invoice {
  898. border: 0.5px solid rgb(135, 195, 255);
  899. margin: 0.2rem;
  900. .deletebtn {
  901. position: absolute;
  902. z-index: 900;
  903. font-size: 24px;
  904. right: 0.08rem;
  905. top: 0.08rem;
  906. color: #c03131;
  907. }
  908. .invoice_item {
  909. border-top: 0.5px solid rgb(135, 195, 255);
  910. }
  911. }
  912. .addinvoice {
  913. padding: 0 0.3rem 0.3rem;
  914. margin-top: 0.2rem;
  915. text-align: right;
  916. }
  917. }
  918. .imgListVan {
  919. display: flex;
  920. flex-direction: column;
  921. }
  922. .vanUploaderImg {
  923. // display: flex;
  924. margin-top: 6px;
  925. /deep/ .van-uploader__preview-image {
  926. width: 70px;
  927. height: 70px;
  928. }
  929. }
  930. /deep/ .van-uploader__upload {
  931. width: 70px;
  932. height: 70px;
  933. }
  934. }
  935. .list {
  936. .list_collapse>div {
  937. margin: 4px;
  938. }
  939. .list_collapse {
  940. .collapse_label_l {
  941. width: 50%;
  942. padding: 4px;
  943. display: inline-block;
  944. }
  945. .collapse_label_r {
  946. width: 50%;
  947. padding: 4px;
  948. display: inline-block;
  949. .waiting {
  950. color: orange;
  951. }
  952. .rejected {
  953. color: red;
  954. }
  955. }
  956. .wrapper {
  957. div {
  958. margin: 8px 16px;
  959. }
  960. div span:nth-child(1) {
  961. width: 30%;
  962. display: inline-block;
  963. }
  964. div span:nth-child(2) {
  965. width: 70%;
  966. display: inline-block;
  967. }
  968. }
  969. .operation {
  970. display: flex;
  971. align-items: center;
  972. justify-content: flex-end;
  973. }
  974. }
  975. }
  976. .audit {
  977. .list_collapse>div {
  978. margin: 4px;
  979. }
  980. .list_collapse {
  981. .collapse_label_l {
  982. width: 60%;
  983. padding: 4px;
  984. display: inline-block;
  985. }
  986. .collapse_label_r {
  987. width: 40%;
  988. padding: 4px;
  989. display: inline-block;
  990. .waiting {
  991. color: orange;
  992. }
  993. .rejected {
  994. color: red;
  995. }
  996. }
  997. .operation {
  998. margin-top: 5px;
  999. padding-top: 5px;
  1000. border-top: 0.5px solid #ebedf0;
  1001. display: flex;
  1002. align-items: center;
  1003. justify-content: flex-end;
  1004. button {
  1005. width: 1.2rem;
  1006. }
  1007. }
  1008. .wrapper {
  1009. div {
  1010. margin: 8px 16px;
  1011. }
  1012. div span:nth-child(1) {
  1013. width: 30%;
  1014. display: inline-block;
  1015. }
  1016. div span:nth-child(2) {
  1017. width: 70%;
  1018. display: inline-block;
  1019. }
  1020. }
  1021. .lookup {
  1022. display: flex;
  1023. align-items: center;
  1024. justify-content: flex-end;
  1025. }
  1026. }
  1027. }
  1028. }
  1029. </style>
  1030. <style>
  1031. .edit_form .invoice .van-field__label {
  1032. color: #999;
  1033. }
  1034. .edit_form .invoice .van-field__control {
  1035. color: #999;
  1036. }
  1037. </style>