index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. <template>
  2. <!-- <el-card class="workspace-info card-border mb-2">
  3. <div class="workspace-name">
  4. <div class="card-header">
  5. <span>{{ form.name }}</span>
  6. </div>
  7. </div>
  8. </el-card> -->
  9. <div class="flex" v-loading="loading">
  10. <el-card class="workspace-info w-full" shadow="always" style="padding-bottom: 40px;">
  11. <template #header>
  12. <div class="card-header" style="display: flex; align-items: center; justify-content: space-between;">
  13. <span style="flex-grow: 1; text-align: center;font-weight: bold;">工作间信息</span>
  14. <div class="pull-right">
  15. <el-button type="primary" @click="openDialog">修改</el-button>
  16. </div>
  17. </div>
  18. </template>
  19. <ul class="user-info">
  20. <div class="info-row">
  21. <li class="info-item">
  22. <Icon class="mr-5px" icon="ep:bell" />
  23. <span class="info-label">工作间名称:</span>
  24. <span class="pull-right">{{ userInfo.name }}</span>
  25. </li>
  26. <li class="info-item">
  27. <Icon class="mr-5px" icon="ep:phone" />
  28. <span class="info-label">联系电话:</span>
  29. <span class="pull-right">{{ userInfo.phone }}</span>
  30. </li>
  31. </div>
  32. <div class="info-row">
  33. <li class="info-item">
  34. <Icon class="mr-5px" icon="fontisto:email" />
  35. <span class="info-label">邮箱:</span>
  36. <span class="pull-right">{{ userInfo.email }}</span>
  37. </li>
  38. <li class="info-item">
  39. <Icon class="mr-5px" icon="ep:location" />
  40. <span class="info-label">工作间地址:</span>
  41. <span class="pull-right">{{ userInfo.address }}</span>
  42. </li>
  43. </div>
  44. <div class="info-row">
  45. <li class="info-item">
  46. <Icon class="mr-5px" icon="ep:user" />
  47. <span class="info-label">导师人数:</span>
  48. <span class="pull-right">{{ userInfo.supervisorNum }}</span>
  49. </li>
  50. <li class="info-item">
  51. <Icon class="mr-5px" icon="ep:user" />
  52. <span class="info-label">学生人数:</span>
  53. <span class="pull-right">{{ userInfo.studentNum }}</span>
  54. </li>
  55. </div>
  56. <!-- <div>
  57. <li class="info-item">
  58. <Icon class="mr-5px" icon="ep:user" />
  59. <span class="info-label">工作间简介:</span>
  60. <span class="pull-right">{{ userInfo.description }}</span>
  61. </li>
  62. </div> -->
  63. </ul>
  64. <div class="info-description">
  65. <!-- <Icon class="mr-5px" icon="ep:location" /> -->
  66. <div style="display: flex; align-items: center;">
  67. <Icon icon="ep:picture" />
  68. <div class="div-label" style="margin-left: 8px;">简介:</div>
  69. </div>
  70. <div class="description-content">{{ cleanedDescription }}</div>
  71. <div class="image-container">
  72. <div v-for="(url, index) in extractedImageUrls" :key="index" class="image-item">
  73. <img :src="url" />
  74. </div>
  75. </div>
  76. </div>
  77. </el-card>
  78. <DeptForm
  79. ref="formRef"
  80. :visible="dialogVisible"
  81. :form="form"
  82. :rules="rules"
  83. @update:visible="dialogVisible = $event"
  84. @success="handleSuccess"
  85. />
  86. </div>
  87. <div>
  88. <div style="margin-top: 15px;">
  89. <teacher :id="form.id" />
  90. </div>
  91. <div>
  92. <student :id="form.id" />
  93. </div>
  94. </div>
  95. </template>
  96. <script lang="ts">
  97. import { defineComponent, reactive, ref, onMounted } from 'vue';
  98. import type { FormRules } from 'element-plus';
  99. // import type { FormExpose } from '@/components/Form'
  100. import { useI18n } from 'vue-i18n';
  101. import { useMessage } from '@/hooks/web/useMessage';
  102. import { updateDept, DeptVO, getUserDept } from '@/api/system/dept/index';
  103. import * as UserApi from '@/api/system/user'
  104. import DeptForm from './DeptForm.vue';
  105. import student from './student.vue';
  106. import teacher from './teacher.vue';
  107. export default defineComponent({
  108. components: {
  109. DeptForm,
  110. student,
  111. teacher,
  112. },
  113. setup() {
  114. const { t } = useI18n();
  115. const message = useMessage();
  116. const loading = ref(true) // 列表的加载中
  117. const dialogVisible = ref(false);
  118. const formRef = ref();
  119. const form = reactive({
  120. id: '',
  121. supervisor: '',
  122. phone: '',
  123. email: '',
  124. address: '',
  125. name: '',
  126. leaderUserId: [],
  127. nickname: '',
  128. description: '',
  129. });
  130. // 表单校验规则
  131. const rules = reactive<FormRules>({
  132. leaderUserId: [
  133. { required: true, message: '请选择导师名称', trigger: 'blur' }
  134. ],
  135. phone: [
  136. { required: true, message: '请输入负责人电话', trigger: 'blur' },
  137. {
  138. pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
  139. message: '请输入正确的手机号码',
  140. trigger: 'blur'
  141. }
  142. ],
  143. email: [
  144. { required: true, message: '请输入邮箱', trigger: 'blur' },
  145. {
  146. type: 'email',
  147. message: '请输入正确的邮箱格式',
  148. trigger: ['blur', 'change']
  149. }
  150. ],
  151. address: [
  152. { required: true, message: '请输入工作间地址', trigger: 'blur' }
  153. ],
  154. name: [
  155. { required: true, message: '请输入工作间名称', trigger: 'blur' }
  156. ],
  157. description: [
  158. { required: false, message: '请输入工作间简介', trigger: 'blur' }
  159. ],
  160. });
  161. // 将 userInfo 定义放入 setup 中
  162. const userInfo = ref({} as DeptVO);
  163. const fetchUserInfo = async () => {
  164. try{
  165. loading.value = true; // 开始加载状态
  166. const users = await getUserDept();
  167. console.log(users,'userDeptInfo');
  168. userInfo.value = {...users,
  169. // name: users.user[0].nickname // 提取 nickname
  170. };
  171. }finally{
  172. loading.value = false; // 关闭加载状态
  173. }
  174. };
  175. // //获取所有导师
  176. // const users = ref()
  177. // const getSupervisor= async () => {
  178. // try {
  179. // const response = await UserApi.getSupervisor()y
  180. // users.value = response
  181. // } catch (error) {
  182. // console.error('Error fetching user data:', error)
  183. // }
  184. // }
  185. // //传supervisorId给form.leaderUserId
  186. // const handleSupervisorChange = (values) => {
  187. // form.leaderUserId = values; // values 是选择的用户ID数组
  188. // }
  189. // const isEditMode = ref(false);
  190. // // 切换编辑模式
  191. // const toggleEditMode = () => {
  192. // if (isEditMode.value) {
  193. // // 在“保存”状态下执行保存操作
  194. // submit();
  195. // }
  196. // isEditMode.value = !isEditMode.value;
  197. // };
  198. const openDialog = () => {
  199. loading.value = true;
  200. dialogVisible.value = true;
  201. formRef.value.open(); // 打开弹窗
  202. loading.value = false; // 关闭加载状态
  203. };
  204. const extractedImageUrls = ref<string[]>([]);
  205. const handleSuccess = async (urls) => {
  206. extractedImageUrls.value = urls;
  207. console.log('提取到的图片URL:', extractedImageUrls);
  208. dialogVisible.value = false;
  209. await fetchUserInfo(); // 更新用户信息
  210. };
  211. const fetchImageUrls = async () => {
  212. try {
  213. loading.value = true; // 开始加载状态
  214. // 你的获取图片 URL 的 API 调用
  215. const res = await getUserDept();
  216. const urls = res.description.match(/<img.*?src="(.*?)"/g); // 使用更合理的正则表达式
  217. // 如果找到的 URL 数组不为空,则进行处理
  218. if (urls) {
  219. // 从匹配的字符串中提取出 URL
  220. extractedImageUrls.value = urls.map(url => {
  221. // 取出匹配到的 src 属性部分
  222. const match = url.match(/src="(.*?)"/);
  223. return match ? match[1] : '';
  224. });
  225. } else {
  226. extractedImageUrls.value = []; // 没有找到图片则设为空数组
  227. } // 将获取到的 URL 赋值
  228. }finally{
  229. loading.value = false; // 关闭加载状态
  230. }
  231. };
  232. // 创建一个 computed 属性来处理并去掉 <p> 标签
  233. const cleanedDescription = computed(() => {
  234. return userInfo.value.description ? userInfo.value.description.replace(/<\/?[^>]+(>|$)/g, '') : '';
  235. });
  236. // 表单提交
  237. const submit = async () => {
  238. try {
  239. await formRef.value?.validate();
  240. console.log('提交的数据:', form);
  241. await updateDept(form).then((res) => {
  242. console.log('更新成功:', res);
  243. });
  244. message.success('成功');
  245. } catch (error) {
  246. console.error('提交错误:', error);
  247. message.error('错误');
  248. }
  249. };
  250. // 表单重置
  251. const init = async () => {
  252. const res = await getUserDept();
  253. console.log('获取的数据:', res);
  254. form.id = res.id;
  255. form.address = res.address;
  256. form.supervisor = res.user.nickname;
  257. form.phone = res.phone;
  258. form.email = res.email;
  259. form.name = res.name;
  260. form.leaderUserId = res.user.id;
  261. form.description = res.description;
  262. userInfo.value = res.user;
  263. };
  264. onMounted(async () => {
  265. await init();
  266. await fetchUserInfo();
  267. // getSupervisor();
  268. await fetchImageUrls(); // 这里 add 一個方法来加载图片
  269. });
  270. return {
  271. t,
  272. form,
  273. userInfo,
  274. loading,
  275. // isEditMode,
  276. // toggleEditMode,
  277. submit,
  278. init,
  279. rules,
  280. formRef,
  281. // users,
  282. // handleSupervisorChange,
  283. // getSupervisor,
  284. cleanedDescription,
  285. dialogVisible,
  286. openDialog,
  287. handleSuccess,
  288. extractedImageUrls,
  289. };
  290. }
  291. });
  292. </script>
  293. <style scoped>
  294. /* .label-bold {
  295. font-weight: bold;
  296. } */
  297. /* .workspace-name {
  298. height: 20px;
  299. } */
  300. .user-info {
  301. margin-top: 10px;
  302. margin-left: 5%;
  303. margin-right: 5%;
  304. padding-right: 0;
  305. padding-left: 0;
  306. border-right: 0;
  307. border-left: 0;
  308. border-radius: 0;
  309. list-style: none;
  310. }
  311. .info-row {
  312. display: flex;
  313. justify-content: space-between; /* 保持并排显示 */
  314. align-items: center; /* 垂直居中对齐 */
  315. margin-bottom: 20px; /* 增加行之间的间距 */
  316. }
  317. .info-item {
  318. flex: 1; /* 每个信息项占据相同的空间 */
  319. padding: 11px 0; /* 内部上下填充 */
  320. margin-right: 50px; /* 每个信息项之间的右边距 */
  321. }
  322. /* 仅为每个信息项添加底部边框 */
  323. .info-item:first-child {
  324. border-bottom: 1px solid #e7eaec;
  325. }
  326. .info-item:last-child {
  327. border-bottom: 1px solid #e7eaec;
  328. }
  329. .info-description {
  330. margin-top: 30px; /* 与上面的信息分隔 */
  331. margin-left: 5%; /* 左侧与上面列表保持一致 */
  332. margin-right: 5%; /* 右侧与上面列表保持一致 */
  333. /* display: flex; */
  334. align-items: center; /* 确保标题与描述对齐 */
  335. align-items: flex-start;
  336. }
  337. .info-label {
  338. font-weight: bold; /* 加粗标题 */
  339. margin-right: 10px; /* 标题与内容之间的间距 */
  340. }
  341. .div-label {
  342. font-weight: bold; /* 加粗标题 */
  343. display: block;
  344. }
  345. .description-content {
  346. white-space: pre-wrap; /* 保持换行 */
  347. overflow-wrap: break-word; /* 自动断行 */
  348. margin-right: 5%;
  349. font-size: 13px; /* 根据需要设置字体大小 */
  350. margin-top: 20px; /* 内容与标题之间不需要额外间距 */
  351. letter-spacing: 3px;
  352. text-indent: 2em;
  353. line-height: 2;
  354. }
  355. .pull-right {
  356. float: right !important;
  357. }
  358. .info-label {
  359. font-weight: bold;
  360. }
  361. /* .personal-info {
  362. margin-top: 20px;
  363. } */
  364. /* .button-container {
  365. display: flex;
  366. justify-content: center;
  367. margin-top: 30px;
  368. } */
  369. .image-container {
  370. display: flex; /* 使图片横向排列 */
  371. flex-wrap: wrap; /* 如果空间不足则换行 */
  372. gap: 10px; /* 图片之间的间距 */
  373. margin-left: 20px; /* 左边距,根据需要设置 */
  374. }
  375. .image-item img {
  376. width: 150px; /* 设置更大的宽度 */
  377. height: auto; /* 保持高度自动 */
  378. }
  379. </style>