index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. <template>
  2. <div class="tenant">
  3. <byTable
  4. :hideTable="true"
  5. :hidePagination="true"
  6. :source="sourceList.data"
  7. :pagination="sourceList.pagination"
  8. :config="config"
  9. :loading="loading"
  10. :selectConfig="selectConfig"
  11. highlight-current-row
  12. :action-list="[
  13. {
  14. text: '默认汇率',
  15. action: () => openModal(),
  16. },
  17. ]"
  18. @get-list="getList">
  19. <template #amount="{ item }">
  20. <div></div>
  21. </template>
  22. </byTable>
  23. <div style="padding: 0 20px 20px 20px; background-color: white" v-if="rateStatus">
  24. <el-table v-loading="loading" :data="sourceList.data">
  25. <el-table-column label="合同编号">
  26. <el-table-column label="" prop="contractCode" width="160" />
  27. </el-table-column>
  28. <el-table-column label="客户名称">
  29. <el-table-column label="" prop="customerName" min-width="200" />
  30. </el-table-column>
  31. <el-table-column label="业务员">
  32. <el-table-column label="" prop="userName" width="120" />
  33. </el-table-column>
  34. <el-table-column label="销售合同金额">
  35. <el-table-column label="" width="120">
  36. <template #default="{ row }">
  37. <div>¥{{ row.contractAmount }}</div>
  38. </template>
  39. </el-table-column>
  40. </el-table-column>
  41. <el-table-column label="收入">
  42. <el-table-column label="合同到账" width="120">
  43. <template #default="{ row }">
  44. <div>¥{{ row.contractArrival }}</div>
  45. </template>
  46. </el-table-column>
  47. <el-table-column label="其他收入" width="120">
  48. <template #default="{ row }">
  49. <div>¥{{ row.otherIncome }}</div>
  50. </template>
  51. </el-table-column>
  52. </el-table-column>
  53. <el-table-column label="采购合同金额">
  54. <el-table-column label="" width="120">
  55. <template #default="{ row }">
  56. <div>¥{{ row.purchaseAmount }}</div>
  57. </template>
  58. </el-table-column>
  59. </el-table-column>
  60. <el-table-column label="支出">
  61. <el-table-column label="支付货款" width="120">
  62. <template #default="{ row }">
  63. <div>¥{{ row.payForGoods }}</div>
  64. </template>
  65. </el-table-column>
  66. <el-table-column label="其他支出" width="120">
  67. <template #default="{ row }">
  68. <div>¥{{ row.otherExpenses }}</div>
  69. </template>
  70. </el-table-column>
  71. </el-table-column>
  72. <el-table-column label="统计">
  73. <el-table-column label="收入合计" width="120">
  74. <template #default="{ row }">
  75. <div>¥{{ row.totalIncome }}</div>
  76. </template>
  77. </el-table-column>
  78. <el-table-column label="支出合计" width="120">
  79. <template #default="{ row }">
  80. <div>¥{{ row.totalExpenses }}</div>
  81. </template>
  82. </el-table-column>
  83. <el-table-column label="毛利" width="120">
  84. <template #default="{ row }">
  85. <div>¥{{ row.grossProfit }}</div>
  86. </template>
  87. </el-table-column>
  88. <el-table-column label="毛利率" prop="grossProfitMargin" width="120">
  89. <template #default="{ row }">
  90. <div style="width: 100%">{{ row.grossProfitMargin }}%</div>
  91. </template>
  92. </el-table-column>
  93. </el-table-column>
  94. <el-table-column label="操作" align="center" width="170" fixed="right">
  95. <template #default="{ row }">
  96. <div>
  97. <el-button type="primary" @click="changeExchangeRate(row)" link>调整汇率</el-button>
  98. <el-button type="primary" @click="clickSettlement(row)" v-if="row.settlementStatus === 0" link>结算</el-button>
  99. <el-button type="primary" @click="clickCancelSettlement(row)" v-else link>取消结算</el-button>
  100. </div>
  101. </template>
  102. </el-table-column>
  103. </el-table>
  104. <el-row style="padding: 20px" justify="end" type="flex">
  105. <el-pagination
  106. background
  107. layout="total, sizes, prev, pager, next, jumper"
  108. :current-page="sourceList.pagination.pageNum"
  109. :page-size="sourceList.pagination.pageSize"
  110. :total="sourceList.pagination.total"
  111. @size-change="handleSizeChange"
  112. @current-change="handlePageChange" />
  113. </el-row>
  114. </div>
  115. <el-dialog title="默认汇率" v-if="dialogVisible" v-model="dialogVisible" width="600">
  116. <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
  117. <template #currencyList>
  118. <el-table :data="formData.data.list" style="width: 100%" v-loading="loadingDialog">
  119. <el-table-column label="币种">
  120. <template #default="{ row }">
  121. <div>{{ dictValueLabel(row.type, accountCurrency) }}</div>
  122. </template>
  123. </el-table-column>
  124. <el-table-column label="兑 CHY 汇率">
  125. <template #default="{ row, $index }">
  126. <el-form-item :prop="'list.' + $index + '.rate'" :rules="rules.rate" :inline-message="true">
  127. <el-input-number v-model="row.rate" placeholder="请输入兑 CHY 汇率" style="width: 100%" :precision="6" :controls="false" :min="0" />
  128. </el-form-item>
  129. </template>
  130. </el-table-column>
  131. </el-table>
  132. </template>
  133. </byForm>
  134. <template #footer>
  135. <el-button @click="dialogVisible = false" size="large">取 消</el-button>
  136. <el-button type="primary" @click="submitForm()" size="large">确 定</el-button>
  137. </template>
  138. </el-dialog>
  139. <el-dialog title="调整汇率" v-if="openChange" v-model="openChange" width="600">
  140. <byForm :formConfig="formChangeConfig" :formOption="formOption" v-model="formChangeData.data" :rules="rules" ref="change">
  141. <template #currencyList>
  142. <el-table :data="formChangeData.data.list" style="width: 100%" v-loading="loadingDialog">
  143. <el-table-column label="币种">
  144. <template #default="{ row }">
  145. <div>{{ dictValueLabel(row.type, accountCurrency) }}</div>
  146. </template>
  147. </el-table-column>
  148. <el-table-column label="兑 CHY 汇率">
  149. <template #default="{ row, $index }">
  150. <el-form-item :prop="'list.' + $index + '.rate'" :rules="rules.rate" :inline-message="true">
  151. <el-input-number v-model="row.rate" placeholder="请输入兑 CHY 汇率" style="width: 100%" :precision="6" :controls="false" :min="0" />
  152. </el-form-item>
  153. </template>
  154. </el-table-column>
  155. </el-table>
  156. </template>
  157. </byForm>
  158. <template #footer>
  159. <el-button @click="openChange = false" size="large">取 消</el-button>
  160. <el-button type="primary" @click="submitChangeForm()" size="large">确 定</el-button>
  161. </template>
  162. </el-dialog>
  163. </div>
  164. </template>
  165. <script setup>
  166. import { computed, ref } from "vue";
  167. import byTable from "@/components/byTable/index";
  168. import byForm from "@/components/byForm/index";
  169. import useUserStore from "@/store/modules/user";
  170. import { ElMessage } from "element-plus";
  171. const { proxy } = getCurrentInstance();
  172. const accountCurrency = ref([]);
  173. const userList = ref([]);
  174. const settlementStatus = ref([
  175. {
  176. label: "未结算",
  177. value: "0",
  178. },
  179. {
  180. label: "已结算",
  181. value: "1",
  182. },
  183. ]);
  184. const sourceList = ref({
  185. data: [],
  186. pagination: {
  187. total: 0,
  188. pageNum: 1,
  189. pageSize: 10,
  190. keyword: "",
  191. settlementStatus: "",
  192. userId: "",
  193. },
  194. });
  195. const loading = ref(false);
  196. const selectConfig = computed(() => {
  197. return [
  198. {
  199. label: "是否完结",
  200. prop: "settlementStatus",
  201. data: settlementStatus.value,
  202. },
  203. {
  204. label: "业务员",
  205. prop: "userId",
  206. data: userList.value,
  207. },
  208. ];
  209. });
  210. const config = computed(() => {
  211. return [];
  212. });
  213. const getDict = () => {
  214. proxy
  215. .post("/dictTenantData/page", {
  216. pageNum: 1,
  217. pageSize: 999,
  218. dictCode: "account_currency",
  219. tenantId: useUserStore().user.tenantId,
  220. })
  221. .then((res) => {
  222. if (res.rows && res.rows.length > 0) {
  223. accountCurrency.value = res.rows.map((item) => {
  224. return {
  225. label: item.dictValue,
  226. value: item.dictKey,
  227. };
  228. });
  229. judgeRate();
  230. } else {
  231. ElMessage("请先添加货币");
  232. }
  233. });
  234. proxy
  235. .get("/tenantUser/list", {
  236. pageNum: 1,
  237. pageSize: 10000,
  238. tenantId: useUserStore().user.tenantId,
  239. })
  240. .then((res) => {
  241. userList.value = res.rows.map((item) => {
  242. return {
  243. label: item.nickName,
  244. value: item.userId,
  245. };
  246. });
  247. });
  248. };
  249. const getList = async (req) => {
  250. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  251. loading.value = true;
  252. proxy.post("/saleStatement/getProfitSettlement", sourceList.value.pagination).then((res) => {
  253. sourceList.value.data = res.rows;
  254. sourceList.value.pagination.total = res.total;
  255. setTimeout(() => {
  256. loading.value = false;
  257. }, 200);
  258. });
  259. };
  260. const rateStatus = ref(false);
  261. const judgeRate = () => {
  262. proxy.post("/currencyRate/list", {}).then(
  263. (res) => {
  264. if (res && res.length > 0) {
  265. for (let i = 0; i < accountCurrency.value.length; i++) {
  266. let currencyStatus = true;
  267. for (let j = 0; j < res.length; j++) {
  268. if (accountCurrency.value[i].value === res[j].type) {
  269. currencyStatus = false;
  270. break;
  271. }
  272. }
  273. if (currencyStatus) {
  274. return ElMessage("请先完成默认汇率的配置");
  275. }
  276. }
  277. rateStatus.value = true;
  278. getList();
  279. } else {
  280. ElMessage("请先完成默认汇率的配置");
  281. }
  282. },
  283. (err) => {
  284. console.log(err);
  285. ElMessage("请先完成默认汇率的配置");
  286. }
  287. );
  288. };
  289. getDict();
  290. const handleSizeChange = (val) => {
  291. sourceList.value.pagination.pageNum = 1;
  292. sourceList.value.pagination.pageSize = val;
  293. getList();
  294. };
  295. const handlePageChange = (val) => {
  296. sourceList.value.pagination.pageNum = val;
  297. getList();
  298. };
  299. const dialogVisible = ref(false);
  300. const loadingDialog = ref(false);
  301. const submit = ref(null);
  302. const formOption = reactive({
  303. inline: true,
  304. labelWidth: 100,
  305. itemWidth: 100,
  306. rules: [],
  307. });
  308. const formData = reactive({
  309. data: {
  310. list: [],
  311. },
  312. });
  313. const formConfig = computed(() => {
  314. return [
  315. {
  316. type: "slot",
  317. prop: "currencyList",
  318. slotName: "currencyList",
  319. label: "",
  320. },
  321. ];
  322. });
  323. const rules = ref({
  324. rate: [{ required: true, message: "请输入兑 CHY 汇率", trigger: "blur" }],
  325. });
  326. const openModal = () => {
  327. if (accountCurrency.value && accountCurrency.value.length > 0) {
  328. formData.data = {
  329. list: accountCurrency.value.map((item) => {
  330. return {
  331. id: "",
  332. type: item.value,
  333. rate: 1,
  334. };
  335. }),
  336. };
  337. } else {
  338. formData.data = {
  339. list: [],
  340. };
  341. }
  342. loadingDialog.value = true;
  343. dialogVisible.value = true;
  344. proxy.post("/currencyRate/list", {}).then(
  345. (res) => {
  346. if (res && res.length > 0 && formData.data.list.length > 0) {
  347. formData.data.list = formData.data.list.map((item) => {
  348. for (let i = 0; i < res.length; i++) {
  349. if (item.type === res[i].type) {
  350. item.id = res[i].id;
  351. item.rate = res[i].rate;
  352. break;
  353. }
  354. }
  355. return {
  356. ...item,
  357. };
  358. });
  359. }
  360. loadingDialog.value = false;
  361. },
  362. (err) => {
  363. console.log(err);
  364. loadingDialog.value = false;
  365. }
  366. );
  367. };
  368. const submitForm = () => {
  369. submit.value.handleSubmit(() => {
  370. loadingDialog.value = true;
  371. proxy.post("/currencyRate/edit", formData.data.list).then(
  372. () => {
  373. ElMessage({
  374. message: "保存成功",
  375. type: "success",
  376. });
  377. dialogVisible.value = false;
  378. rateStatus.value = true;
  379. getList();
  380. },
  381. (err) => {
  382. console.log(err);
  383. loadingDialog.value = false;
  384. }
  385. );
  386. });
  387. };
  388. const openChange = ref(false);
  389. const change = ref(null);
  390. const formChangeData = reactive({
  391. data: {
  392. list: [],
  393. },
  394. });
  395. const formChangeConfig = computed(() => {
  396. return [
  397. {
  398. type: "slot",
  399. prop: "currencyList",
  400. slotName: "currencyList",
  401. label: "",
  402. },
  403. ];
  404. });
  405. const changeExchangeRate = (row) => {
  406. formChangeData.data = {
  407. id: row.contractId,
  408. list: [],
  409. };
  410. if (accountCurrency.value && accountCurrency.value.length > 0) {
  411. formChangeData.data.list = accountCurrency.value.map((item) => {
  412. return {
  413. type: item.value,
  414. rate: 1,
  415. };
  416. });
  417. }
  418. loadingDialog.value = true;
  419. openChange.value = true;
  420. if (row.currencyRateJson) {
  421. let currencyRateJson = JSON.parse(row.currencyRateJson);
  422. formChangeData.data.list = formChangeData.data.list.map((item) => {
  423. for (let i = 0; i < currencyRateJson.length; i++) {
  424. if (item.type === currencyRateJson[i].type) {
  425. item.rate = currencyRateJson[i].rate;
  426. break;
  427. }
  428. }
  429. return {
  430. ...item,
  431. };
  432. });
  433. loadingDialog.value = false;
  434. } else {
  435. proxy.post("/currencyRate/list", {}).then(
  436. (res) => {
  437. if (res && res.length > 0 && formChangeData.data.list.length > 0) {
  438. formChangeData.data.list = formChangeData.data.list.map((item) => {
  439. for (let i = 0; i < res.length; i++) {
  440. if (item.type === res[i].type) {
  441. item.rate = res[i].rate;
  442. break;
  443. }
  444. }
  445. return {
  446. ...item,
  447. };
  448. });
  449. }
  450. loadingDialog.value = false;
  451. },
  452. (err) => {
  453. console.log(err);
  454. loadingDialog.value = false;
  455. }
  456. );
  457. }
  458. };
  459. const submitChangeForm = () => {
  460. change.value.handleSubmit(() => {
  461. loadingDialog.value = true;
  462. let data = {};
  463. data.id = formChangeData.data.id;
  464. data.currencyRateJson = JSON.stringify(formChangeData.data.list);
  465. proxy.post("/commission/add", data).then(
  466. () => {
  467. ElMessage({
  468. message: "保存成功",
  469. type: "success",
  470. });
  471. openChange.value = false;
  472. getList();
  473. },
  474. (err) => {
  475. console.log(err);
  476. loadingDialog.value = false;
  477. }
  478. );
  479. });
  480. };
  481. const clickSettlement = (row) => {
  482. let data = proxy.deepClone(row);
  483. data.afterSalesAmount = 0;
  484. data.publicAmount = 0;
  485. data.haveOverallAmount = 0;
  486. data.departmentalCommission = 0;
  487. data.personalCommission = 0;
  488. data.settlementStatus = 1;
  489. proxy.post("/commission/add", data).then(() => {
  490. ElMessage({
  491. message: "保存成功",
  492. type: "success",
  493. });
  494. getList();
  495. });
  496. };
  497. const clickCancelSettlement = (row) => {
  498. proxy.post("/commission/add", { id: row.contractId, settlementStatus: 0 }).then(() => {
  499. ElMessage({
  500. message: "保存成功",
  501. type: "success",
  502. });
  503. getList();
  504. });
  505. };
  506. </script>
  507. <style lang="scss" scoped>
  508. .tenant {
  509. padding: 20px;
  510. }
  511. ::v-deep(.el-input-number .el-input__inner) {
  512. text-align: left;
  513. }
  514. </style>