SkuGroupPage.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. <template>
  2. <q-page padding>
  3. <q-dialog v-model="popupBindDestinationWindow">
  4. <q-card>
  5. <q-card-section>
  6. <div class="text-h6">产品-目的地关联</div>
  7. </q-card-section>
  8. <q-card-section>
  9. <div class="text-h6">
  10. <q-input
  11. outlined
  12. @update:model-value="(val) => goFilterDestination(val)"
  13. :debounce="500"
  14. v-model="filterDestination"
  15. label="Filter Destination"
  16. />
  17. </div>
  18. </q-card-section>
  19. <q-separator />
  20. <div style="width: 50vw; height: 70vh; display: flex">
  21. <div style="width: 38%; max-width: 38%; height: 100%">
  22. <label style="color: red; font-weight: bold">候选目的地</label>
  23. <q-list bordered separator>
  24. <q-item
  25. clickable
  26. v-ripple
  27. :active="item.destinationId === selected_left_destination"
  28. @click="setActiveSelectLeft(item.destinationId)"
  29. active-class="bg-teal-1 text-grey-8"
  30. v-for="item in candidateDestinations"
  31. v-bind:key="item.destinationId"
  32. >
  33. <q-item-section>
  34. <q-item-label
  35. >目的地: {{ item.destinationName }}</q-item-label
  36. >
  37. </q-item-section>
  38. </q-item>
  39. </q-list>
  40. </div>
  41. <div style="min-width: 10px; height: 100%">
  42. <div>
  43. <q-btn
  44. style="margin-top: 10px"
  45. round
  46. color="secondary"
  47. icon="navigate_next"
  48. @click="doBind"
  49. />
  50. </div>
  51. <div>
  52. <q-btn
  53. style="margin-top: 10px"
  54. round
  55. color="secondary"
  56. @click="unBind"
  57. icon="navigate_before"
  58. />
  59. </div>
  60. </div>
  61. <div style="width: 38%; height: 100%">
  62. <label style="color: red; font-weight: bold">已关联目的地</label>
  63. <q-list bordered separator>
  64. <q-item
  65. clickable
  66. v-ripple
  67. :active="item.destinationId === selected_right_destination"
  68. @click="setActiveSelectRight(item.destinationId)"
  69. active-class="bg-teal-1 text-grey-8"
  70. v-for="item in bindedDestinations"
  71. v-bind:key="item.destinationId"
  72. ou
  73. >
  74. <q-item-section>
  75. <q-item-label
  76. >目的地: {{ item.destinationName }}</q-item-label
  77. >
  78. </q-item-section>
  79. </q-item>
  80. </q-list>
  81. </div>
  82. </div>
  83. </q-card>
  84. </q-dialog>
  85. <div class="row justify-center q-pa-md">
  86. <q-table
  87. ref="skuGroupTableRef"
  88. style="max-width: 98%; min-width: 90%"
  89. title="产品管理"
  90. :rows="skuGroups"
  91. :columns="columns"
  92. v-model:pagination="serverPagination"
  93. :loading="loading"
  94. :filter="filter"
  95. row-key="skuGroupId"
  96. :separator="'cell'"
  97. @request="onRequest"
  98. >
  99. <template v-slot:top-right>
  100. <q-input
  101. borderless
  102. dense
  103. debounce="300"
  104. v-model="filter"
  105. placeholder="Search"
  106. >
  107. <template v-slot:append>
  108. <q-icon name="search" />
  109. </template>
  110. </q-input>
  111. </template>
  112. <template v-slot:body-cell-skuGroupId="props">
  113. <q-td :props="props">
  114. <q-btn
  115. v-if="props.row.enabled == false"
  116. color="blue-grey-3"
  117. style="cursor: pointer"
  118. @click="copyText(props.row.skuGroupId)"
  119. type="button"
  120. >{{ props.row.skuGroupId }}</q-btn
  121. >
  122. <q-btn
  123. v-if="props.row.enabled == true"
  124. color="green"
  125. style="cursor: pointer"
  126. @click="copyText(props.row.skuGroupId)"
  127. type="button"
  128. >{{ props.row.skuGroupId }}</q-btn
  129. >
  130. </q-td>
  131. </template>
  132. <template v-slot:body-cell-action="props">
  133. <q-td :props="props">
  134. <q-btn
  135. v-if="props.row.enabled == false"
  136. color="purple"
  137. dense
  138. label="启用"
  139. @click="enableGroup(props.row.skuGroupId)"
  140. />
  141. <q-btn
  142. v-if="props.row.enabled == true"
  143. color="purple"
  144. dense
  145. label="停用"
  146. @click="disableGroup(props.row.skuGroupId)"
  147. />
  148. <q-btn
  149. color="orange"
  150. dense
  151. label="绑定目的地"
  152. @click="bindDestination(props.row.skuGroupId)"
  153. />
  154. </q-td>
  155. </template>
  156. </q-table></div
  157. ></q-page>
  158. </template>
  159. <script>
  160. import { api } from "boot/axios";
  161. import { useQuasar } from "quasar";
  162. export default {
  163. name: "SkuGroupPage",
  164. setup() {
  165. const $q = useQuasar();
  166. return {
  167. onRowClick: (row) => alert(`${row.name} clicked`),
  168. showNotifyMessageFail(msg) {
  169. $q.notify({
  170. message: msg,
  171. color: "red",
  172. position: "top-right",
  173. });
  174. },
  175. showNotifyMessageSucceed(msg) {
  176. $q.notify({
  177. message: msg,
  178. color: "green",
  179. position: "top-right",
  180. });
  181. },
  182. };
  183. },
  184. mounted() {
  185. this.$refs.skuGroupTableRef.requestServerInteraction();
  186. },
  187. methods: {
  188. unBind() {
  189. if (!this.selected_right_destination?.trim()) {
  190. alert("Haven't selected anything");
  191. return;
  192. }
  193. console.log("unbind: " + this.selected_right_destination);
  194. var params = {};
  195. params.skuGroupId = this.currentSkuGroupId;
  196. params.destinationId = this.selected_right_destination;
  197. api
  198. .delete("/api/v1/destination/unbind-skugroup-destination", {
  199. data: params,
  200. })
  201. .then((response) => {
  202. if (response.data.ok == true) {
  203. this.showNotifyMessageSucceed(response.data.message);
  204. this.loadBindedDestinations(this.currentSkuGroupId);
  205. } else {
  206. this.showNotifyMessageFail(response.data.message);
  207. }
  208. this.selected_right_destination = "";
  209. })
  210. .catch((e) => {
  211. this.showNotifyMessageFail(e.toString());
  212. });
  213. },
  214. doBind() {
  215. if (!this.selected_left_destination?.trim()) {
  216. alert("Haven't selected anything");
  217. return;
  218. }
  219. var params = {};
  220. params.skuGroupId = this.currentSkuGroupId;
  221. params.destinationId = this.selected_left_destination;
  222. api
  223. .post("/api/v1/destination/bind-skugroup-destination", params)
  224. .then((response) => {
  225. if (response.data.ok == true) {
  226. this.showNotifyMessageSucceed(response.data.message);
  227. this.loadBindedDestinations(this.currentSkuGroupId);
  228. } else {
  229. this.showNotifyMessageFail(response.data.message);
  230. }
  231. this.selected_left_destination = "";
  232. })
  233. .catch((e) => {
  234. this.showNotifyMessageFail(e.toString());
  235. });
  236. },
  237. setActiveSelectRight(val) {
  238. this.selected_right_destination = val;
  239. },
  240. setActiveSelectLeft(val) {
  241. this.selected_left_destination = val;
  242. },
  243. goFilterDestination(val) {
  244. console.log(val);
  245. var params = {};
  246. params.query = val;
  247. api
  248. .get("/api/v1/destination/filter-short", {
  249. params: params,
  250. })
  251. .then((response) => {
  252. if (response.data.ok == true) {
  253. this.candidateDestinations = response.data.data;
  254. } else {
  255. this.candidateDestinations = [];
  256. }
  257. })
  258. .catch((e) => {
  259. console.log(e);
  260. this.showNotifyMessageFail(e.response);
  261. });
  262. },
  263. loadBindedDestinations(skuGroupId) {
  264. console.log(
  265. "loading already binded destination for product sku group:" + skuGroupId
  266. );
  267. // after this, should have bindedDestinations ready, once loaded
  268. api
  269. .get("/api/v1/destination/sku-group-binded/" + this.currentSkuGroupId)
  270. .then((response) => {
  271. console.log("get all binded:");
  272. console.log(response.data.data);
  273. if (response.data.ok === true) {
  274. this.bindedDestinations = response.data.data;
  275. } else {
  276. this.bindedDestinations = [];
  277. }
  278. })
  279. .catch((e) => {
  280. console.log(e);
  281. this.showNotifyMessageFail(e.response);
  282. });
  283. },
  284. bindDestination(skuGroupId) {
  285. this.currentSkuGroupId = skuGroupId;
  286. this.selected_left_destination = "";
  287. this.selected_right_destination = "";
  288. this.filterDestination = "";
  289. this.candidateDestinations = [];
  290. this.loadBindedDestinations(skuGroupId);
  291. this.popupBindDestinationWindow = true;
  292. },
  293. copyText(text) {
  294. const unsecuredCopyToClipboard = (text) => {
  295. const textArea = document.createElement("textarea");
  296. textArea.value = text;
  297. document.body.appendChild(textArea);
  298. textArea.focus();
  299. textArea.select();
  300. try {
  301. document.execCommand("copy");
  302. } catch (err) {
  303. console.error("Unable to copy to clipboard", err);
  304. }
  305. document.body.removeChild(textArea);
  306. };
  307. if (window.isSecureContext && navigator.clipboard) {
  308. navigator.clipboard.writeText(text);
  309. this.showNotifyMessageSucceed("成功复制" + text + "到剪贴板");
  310. } else {
  311. unsecuredCopyToClipboard(text);
  312. this.showNotifyMessageSucceed("成功复制" + text + "到剪贴板");
  313. }
  314. },
  315. enableGroup(skuGroupId) {
  316. api
  317. .put("/api/v1/skugroup/enable/" + skuGroupId)
  318. .then((response) => {
  319. if (response.data.ok === true) {
  320. this.showNotifyMessageSucceed(response.data.message);
  321. this.$refs.skuGroupTableRef.requestServerInteraction();
  322. } else {
  323. this.showNotifyMessageFail(response.data.message);
  324. }
  325. })
  326. .catch((e) => {
  327. storeThis.showNotifyMessageFail(e.toString());
  328. });
  329. },
  330. disableGroup(skuGroupId) {
  331. api
  332. .put("/api/v1/skugroup/disable/" + skuGroupId)
  333. .then((response) => {
  334. if (response.data.ok === true) {
  335. this.showNotifyMessageSucceed(response.data.message);
  336. this.$refs.skuGroupTableRef.requestServerInteraction();
  337. } else {
  338. this.showNotifyMessageFail(response.data.message);
  339. }
  340. })
  341. .catch((e) => {
  342. storeThis.showNotifyMessageFail(e.toString());
  343. });
  344. },
  345. onRequest(props) {
  346. const { page, rowsPerPage } = props.pagination;
  347. const filter = props.filter;
  348. var storeThis = this;
  349. this.loading = true;
  350. var params = {};
  351. params.page = page;
  352. params.pageSize = rowsPerPage;
  353. params.query = filter;
  354. api
  355. .get("/api/v1/skugroup/groupstats", {
  356. params: params,
  357. })
  358. .then((response) => {
  359. this.skuGroups = response.data.data.data;
  360. // console.log("this is the group stats I got:");
  361. // console.log(this.skuGroups);
  362. this.serverPagination.page = response.data.data.currentPage;
  363. this.serverPagination.rowsNumber = response.data.data.total;
  364. this.serverPagination.rowsPerPage = response.data.data.pageSize;
  365. this.loading = false;
  366. })
  367. .catch((e) => {
  368. console.log(e);
  369. storeThis.showNotifyMessageFail(e.response);
  370. });
  371. },
  372. },
  373. data() {
  374. return {
  375. bindedDestinations: [],
  376. currentSkuGroupId: "",
  377. selected_left_destination: "",
  378. selected_right_destination: "",
  379. filterDestination: "",
  380. popupBindDestinationWindow: false,
  381. candidateDestinations: [],
  382. selected: [],
  383. filter: "",
  384. loading: false,
  385. serverPagination: {
  386. page: 1,
  387. rowsPerPage: 100,
  388. rowsNumber: 10, // specifying this determines pagination is server-side
  389. },
  390. skuGroups: [],
  391. columns: [
  392. {
  393. name: "skuGroupId",
  394. required: true,
  395. label: "SKU组ID",
  396. align: "left",
  397. field: "skuGroupId",
  398. sortable: false,
  399. style:
  400. "max-width: 20vw;text-overflow: ellipsis !important;white-space: nowrap !important;overflow: hidden !important;",
  401. },
  402. {
  403. name: "skuGroupName",
  404. required: true,
  405. label: "SKU组名(同产品名)",
  406. align: "left",
  407. field: "skuGroupName",
  408. sortable: false,
  409. style:
  410. "max-width: 20vw;text-overflow: ellipsis !important;white-space: nowrap !important;overflow: hidden !important;",
  411. },
  412. {
  413. name: "productTypeName",
  414. required: true,
  415. label: "产品类型",
  416. align: "left",
  417. field: "productTypeName",
  418. sortable: false,
  419. style:
  420. "max-width: 20vw;text-overflow: ellipsis !important;white-space: nowrap !important;overflow: hidden !important;",
  421. },
  422. {
  423. name: "productCount",
  424. required: true,
  425. label: "SKU组成数",
  426. align: "left",
  427. field: "productCount",
  428. sortable: false,
  429. style:
  430. "max-width: 20vw;text-overflow: ellipsis !important;white-space: nowrap !important;overflow: hidden !important;",
  431. },
  432. { name: "action", label: "操作", field: "action" },
  433. ],
  434. };
  435. },
  436. };
  437. </script>