CarouselGroupSelector.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. <template>
  2. <div>
  3. <el-button type="primary" @click="dialogVisible = true">选择轮播组</el-button>
  4. <div v-if="showSelected && selectedGroup" class="selected-group">
  5. <el-tag closable @close="removeGroup" style="margin: 2px">
  6. {{ selectedGroup.name }}
  7. </el-tag>
  8. </div>
  9. <el-dialog title="选择轮播组" v-model="dialogVisible" width="900px" append-to-body>
  10. <el-form :inline="true" :model="queryParams" class="mb-2">
  11. <el-form-item label="名称">
  12. <el-input v-model="queryParams.itemName" placeholder="轮播组名称" clearable style="width: 180px"
  13. @keyup.enter="getCarouselList" />
  14. </el-form-item>
  15. <el-form-item>
  16. <el-button type="primary" @click="getCarouselList">搜索</el-button>
  17. </el-form-item>
  18. </el-form>
  19. <el-table v-loading="dialogLoading" :data="carouselList" @row-click="handleRowClick" highlight-current-row>
  20. <el-table-column width="50">
  21. <template #default="{ row }">
  22. <el-checkbox v-model="row.selected" @click.stop @change="handleSelectChange(row)" />
  23. </template>
  24. </el-table-column>
  25. <el-table-column prop="name" label="轮播组名称" min-width="150" />
  26. <el-table-column prop="itemCount" label="轮播项数量" width="100" />
  27. <el-table-column prop="duration" label="轮播间隔(秒)" width="120" />
  28. <el-table-column prop="createTime" label="创建时间" width="180" />
  29. </el-table>
  30. <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
  31. v-model:limit="queryParams.pageSize" @pagination="getCarouselList" />
  32. <template #footer>
  33. <el-button @click="dialogVisible = false">取消</el-button>
  34. <el-button type="primary" @click="confirmSelect">确定</el-button>
  35. </template>
  36. </el-dialog>
  37. </div>
  38. </template>
  39. <script setup lang="ts">
  40. import { ref, watch, defineProps, defineEmits, nextTick } from 'vue';
  41. import { listItem } from '@/api/smsb/source/item';
  42. import type { ItemVO, ItemQuery } from '@/api/smsb/source/item_type';
  43. export interface CarouselGroup {
  44. id: string | number;
  45. name: string;
  46. itemCount: number;
  47. duration: number;
  48. createTime: string;
  49. items: any[];
  50. selected?: boolean;
  51. [key: string]: any; // Allow additional properties
  52. }
  53. const props = defineProps<{
  54. modelValue: string;
  55. showSelected?: boolean;
  56. }>();
  57. const emit = defineEmits(['update:modelValue']);
  58. const dialogVisible = ref(false);
  59. const dialogLoading = ref(false);
  60. const carouselList = ref<CarouselGroup[]>([]);
  61. const total = ref(0);
  62. const currentRow = ref<CarouselGroup | null>(null);
  63. const selectedGroup = ref<CarouselGroup | null>(null);
  64. // 定义查询参数类型
  65. interface CarouselQuery extends ItemQuery {
  66. pageNum: number;
  67. pageSize: number;
  68. itemName: string;
  69. itemType: number;
  70. }
  71. const queryParams = ref<CarouselQuery>({
  72. pageNum: 1,
  73. pageSize: 10,
  74. itemName: '',
  75. itemType: 1 // 1表示轮播组类型
  76. });
  77. // 初始化时,如果有值,反序列化
  78. watch(
  79. () => props.modelValue,
  80. (val) => {
  81. if (val) {
  82. try {
  83. // 使用 JSON.parse 的 reviver 函数确保大数 ID 保持为字符串
  84. const parsedValue = JSON.parse(val, (key, value) => {
  85. // 如果键是 id,确保它作为字符串返回
  86. if (key === 'id' && value !== null && value !== undefined) {
  87. return String(value);
  88. }
  89. return value;
  90. });
  91. // 确保 id 是字符串
  92. const id = parsedValue.id ? String(parsedValue.id) : '';
  93. selectedGroup.value = {
  94. id: id,
  95. name: String(parsedValue.name || ''),
  96. itemCount: Number(parsedValue.itemCount) || 0,
  97. duration: Number(parsedValue.duration) || 5,
  98. createTime: String(parsedValue.createTime || ''),
  99. items: Array.isArray(parsedValue.items) ? parsedValue.items : []
  100. };
  101. } catch (error) {
  102. console.error('Error parsing modelValue:', error);
  103. selectedGroup.value = null;
  104. }
  105. } else {
  106. selectedGroup.value = null;
  107. }
  108. },
  109. { immediate: true, deep: true }
  110. );
  111. // 查询轮播组列表
  112. const getCarouselList = async () => {
  113. dialogLoading.value = true;
  114. try {
  115. // 构建查询参数,确保类型安全
  116. const query: ItemQuery = {
  117. itemName: queryParams.value.itemName,
  118. itemType: 1, // 1表示轮播组类型
  119. pageNum: queryParams.value.pageNum,
  120. pageSize: queryParams.value.pageSize
  121. };
  122. const res = await listItem(query);
  123. // 转换数据格式,并标记已选中的项,确保 ID 是字符串
  124. carouselList.value = (res.rows || []).map((item: ItemVO) => {
  125. // 确保 ID 是字符串,避免大数精度问题
  126. const itemId = item.id ? String(item.id) : '';
  127. const isSelected = selectedGroup.value?.id === itemId;
  128. return {
  129. id: itemId,
  130. name: item.itemName || '',
  131. itemName: item.itemName || '', // 保持与API返回的字段名一致
  132. itemCount: item.sourceNum || 0,
  133. duration: 5, // 默认5秒,可以根据实际需求调整
  134. createTime: item.createTime || '',
  135. selected: isSelected,
  136. items: [] // 不需要在UI中显示,仅用于内部处理
  137. };
  138. });
  139. // 如果当前有选中的组,确保它在列表中也被选中
  140. if (selectedGroup.value?.id) {
  141. const selectedItem = carouselList.value.find((item) => item.id === selectedGroup.value?.id);
  142. if (selectedItem) {
  143. selectedItem.selected = true;
  144. currentRow.value = selectedItem;
  145. }
  146. }
  147. total.value = res.total || 0;
  148. } finally {
  149. dialogLoading.value = false;
  150. }
  151. };
  152. // 行点击
  153. const handleRowClick = (row: CarouselGroup) => {
  154. currentRow.value = row;
  155. };
  156. // 处理选择变化
  157. const handleSelectChange = (row: CarouselGroup) => {
  158. if (row.selected) {
  159. // 只允许单选,取消其他项
  160. carouselList.value.forEach((item) => {
  161. if (item !== row) item.selected = false;
  162. });
  163. currentRow.value = row;
  164. } else {
  165. // 当前项取消选中
  166. currentRow.value = null;
  167. }
  168. };
  169. // 选择轮播组
  170. const selectGroup = (row: CarouselGroup) => {
  171. row.selected = true;
  172. handleSelectChange(row);
  173. confirmSelect();
  174. };
  175. // 移除已选轮播组
  176. const removeGroup = () => {
  177. selectedGroup.value = null;
  178. emit('update:modelValue', '');
  179. };
  180. // 确认选择
  181. const confirmSelect = () => {
  182. console.log('confirmSelect called, currentRow:', currentRow.value);
  183. if (!currentRow.value) {
  184. // 无选中项,清除选中
  185. console.log('No row selected, clearing selection');
  186. selectedGroup.value = null;
  187. emit('update:modelValue', '');
  188. dialogVisible.value = false;
  189. return;
  190. }
  191. // 保持ID为字符串,避免精度丢失
  192. const id = String(currentRow.value.id);
  193. console.log('Selected group ID (as string):', id, 'Type:', typeof id);
  194. if (!id) {
  195. console.error('Invalid group ID:', currentRow.value.id);
  196. return;
  197. }
  198. // 只存储 id 和 name 字段,确保id是字符串
  199. const selectedData = {
  200. id: id, // 已经是字符串
  201. name: String(currentRow.value.name || '') // 确保name是字符串
  202. };
  203. console.log('Emitting selected data:', selectedData);
  204. selectedGroup.value = selectedData;
  205. // 更新所有行的选中状态
  206. carouselList.value.forEach((item) => {
  207. item.selected = String(item.id) === id; // 确保比较时类型一致
  208. });
  209. emit('update:modelValue', JSON.stringify(selectedData));
  210. dialogVisible.value = false;
  211. };
  212. // 弹窗首次打开时自动加载
  213. watch(dialogVisible, (val) => {
  214. if (val) getCarouselList();
  215. });
  216. </script>
  217. <style scoped>
  218. .selected-group {
  219. margin-top: 8px;
  220. }
  221. </style>