edit.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. <script setup lang="ts">
  2. import { useLoading } from '/@/utils/loading-util'
  3. import { computed, onMounted, ref } from 'vue'
  4. import DashboardDesigner from '/@/components/assistant/DashboardDesigner.vue'
  5. import ComponentLibrary from '/@/components/assistant/ComponentLibrary.vue'
  6. import { ElMessage } from 'element-plus'
  7. import type { MarkdownDashBoard, Position, Size, Content, AddCardData, ComponentLibraryItem } from '/@/components/assistant/types'
  8. // 预留props,暂时不使用
  9. const props = defineProps<{
  10. id?: number
  11. }>()
  12. const data = ref<MarkdownDashBoard[]>([])
  13. // 预留加载功能,暂时不使用
  14. const { loading: loadingDashboard, doLoading: doLoadingDashBoard } = useLoading(async () => {
  15. if (props.id === undefined) {
  16. return
  17. }
  18. await new Promise((resolve) => setTimeout(resolve, 1000))
  19. //TODO fetch remote
  20. })
  21. onMounted(doLoadingDashBoard)
  22. const renderer = computed<MarkdownDashBoard[]>(() => [...data.value].sort((a, b) => a.z - b.z))
  23. // 生成唯一ID
  24. const generateId = () => {
  25. return `card-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`
  26. }
  27. const { loading: loadingDashboardSubmit, doLoading: doLoadingDashboardSubmit } = useLoading(async () => {
  28. try {
  29. await new Promise((resolve) => setTimeout(resolve, 1000))
  30. // TODO: 实际的保存逻辑
  31. // 这里应该调用API保存data.value到后端
  32. ElMessage.success('仪表板保存成功')
  33. } catch (error) {
  34. ElMessage.error('保存失败')
  35. throw error
  36. }
  37. })
  38. // 添加新卡片
  39. const addCard = (cardData: AddCardData) => {
  40. const newCard: MarkdownDashBoard = {
  41. id: generateId(),
  42. x: cardData.x ?? Math.random() * 50, // 使用传入位置或随机位置
  43. y: cardData.y ?? Math.random() * 50,
  44. w: 30,
  45. h: 25,
  46. z: Math.max(...data.value.map((item) => item.z), 0) + 1,
  47. title: cardData.title,
  48. data: cardData.data,
  49. }
  50. data.value.push(newCard)
  51. }
  52. // 更新卡片位置
  53. const updateCardPosition = (id: string, position: Position) => {
  54. const card = data.value.find((item) => item.id === id)
  55. if (card) {
  56. card.x = position.x
  57. card.y = position.y
  58. }
  59. }
  60. // 更新卡片大小
  61. const updateCardSize = (id: string, size: Size) => {
  62. const card = data.value.find((item) => item.id === id)
  63. if (card) {
  64. card.w = size.w
  65. card.h = size.h
  66. }
  67. }
  68. // 更新卡片内容
  69. const updateCardContent = (id: string, content: Content) => {
  70. const card = data.value.find((item) => item.id === id)
  71. if (card) {
  72. card.title = content.title
  73. card.data = content.data
  74. }
  75. }
  76. // 删除卡片
  77. const removeCard = (id: string) => {
  78. const index = data.value.findIndex((item) => item.id === id)
  79. if (index !== -1) {
  80. data.value.splice(index, 1)
  81. }
  82. }
  83. // 组件库数据
  84. const library = ref<ComponentLibraryItem[]>([
  85. {
  86. id: 'quote-block',
  87. title: '引用块',
  88. icon: 'ele-Document',
  89. description: '用于显示引用内容',
  90. data: `> ### 重要提示
  91. >
  92. > 这是一个引用块示例,可以用来显示重要信息、提示或者引用其他内容。
  93. >
  94. > 支持多行内容和**格式化文本**。`,
  95. preview: `> 这是一个引用块示例\n> 可以包含重要信息`,
  96. },
  97. {
  98. id: 'data-table',
  99. title: '数据表格',
  100. icon: 'ele-Document',
  101. description: '用于显示结构化数据',
  102. data: `| 指标名称 | 当前值 | 目标值 | 完成率 |
  103. |----------|--------|--------|--------|
  104. | 用户注册 | 1,234 | 1,500 | 82.3% |
  105. | 活跃用户 | 856 | 1,000 | 85.6% |
  106. | 转化率 | 12.5% | 15% | 83.3% |
  107. | 收入 | ¥45,678 | ¥50,000 | 91.4% |`,
  108. preview: `| 指标 | 数值 | 状态 |\n|------|------|------|\n| 用户 | 1,234 | 正常 |`,
  109. },
  110. ])
  111. //
  112. </script>
  113. <template>
  114. <div class="dashboard-edit-container">
  115. <!-- 顶部工具栏 -->
  116. <div class="toolbar">
  117. <div class="toolbar-left">
  118. <h2>仪表板设计器</h2>
  119. </div>
  120. <div class="toolbar-right">
  121. <el-button type="primary" @click="doLoadingDashboardSubmit" :loading="loadingDashboardSubmit" :disabled="loadingDashboard">
  122. 保存仪表板
  123. </el-button>
  124. </div>
  125. </div>
  126. <!-- 主要内容区域 -->
  127. <div class="main-content" v-loading="loadingDashboard">
  128. <!-- 左侧设计器面板 -->
  129. <div class="designer-panel">
  130. <DashboardDesigner
  131. :cards="renderer"
  132. @update-position="updateCardPosition"
  133. @update-size="updateCardSize"
  134. @update-content="updateCardContent"
  135. @remove-card="removeCard"
  136. @add-card="addCard"
  137. />
  138. </div>
  139. <!-- 右侧组件库面板 -->
  140. <div class="library-panel">
  141. <ComponentLibrary :library="library" @add-card="addCard" />
  142. </div>
  143. </div>
  144. </div>
  145. </template>
  146. <style scoped lang="scss">
  147. .dashboard-edit-container {
  148. height: 100vh;
  149. display: flex;
  150. flex-direction: column;
  151. background: var(--el-bg-color-page);
  152. }
  153. .toolbar {
  154. display: flex;
  155. justify-content: space-between;
  156. align-items: center;
  157. padding: 16px 24px;
  158. background: var(--el-bg-color);
  159. border-bottom: 1px solid var(--el-border-color-light);
  160. box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
  161. .toolbar-left {
  162. h2 {
  163. margin: 0;
  164. color: var(--el-text-color-primary);
  165. font-size: 18px;
  166. font-weight: 600;
  167. }
  168. }
  169. .toolbar-right {
  170. display: flex;
  171. gap: 12px;
  172. }
  173. }
  174. .main-content {
  175. flex: 1;
  176. display: flex;
  177. overflow: hidden;
  178. }
  179. .designer-panel {
  180. flex: 1;
  181. background: var(--el-bg-color);
  182. border-right: 1px solid var(--el-border-color-light);
  183. }
  184. .library-panel {
  185. width: 300px;
  186. background: var(--el-bg-color);
  187. border-left: 1px solid var(--el-border-color-light);
  188. }
  189. </style>