@jx3box/jx3box-ui 2.0.5 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/.storybook/preview.js +22 -13
  2. package/.storybook/storybookMocks.js +191 -0
  3. package/README.md +6 -19
  4. package/assets/css/bread/admin.less +20 -2
  5. package/assets/css/bread/breadcrumb.less +5 -2
  6. package/assets/css/comment/quick-reply.less +1 -1
  7. package/assets/css/common/left-sidebar.less +1 -1
  8. package/assets/css/interact/like2.less +5 -2
  9. package/assets/css/single/author.less +15 -10
  10. package/assets/css/single/cms-single.less +1 -0
  11. package/assets/css/single/thx.less +34 -11
  12. package/assets/data/nav.json +344 -10
  13. package/assets/img/comment/comment.svg +1 -1
  14. package/assets/img/comment/heart_1.svg +1 -1
  15. package/assets/img/comment/hide.svg +1 -1
  16. package/assets/img/leftsidebar/medal.svg +1 -1
  17. package/assets/img/leftsidebar/post.svg +27 -46
  18. package/i18n/messages/en-US.js +357 -22
  19. package/i18n/messages/vi.js +350 -22
  20. package/i18n/messages/zh-CN.js +469 -20
  21. package/i18n/messages/zh-TW.js +350 -22
  22. package/index.js +19 -7
  23. package/package.json +8 -5
  24. package/service/cms.js +22 -0
  25. package/service/commit-history.js +18 -0
  26. package/service/community.js +60 -0
  27. package/service/fav.js +5 -6
  28. package/src/App.vue +154 -80
  29. package/src/Breadcrumb.vue +24 -11
  30. package/src/CommonFooter.vue +2 -2
  31. package/src/Footer.vue +13 -5
  32. package/src/LeftSideToggle.vue +10 -3
  33. package/src/LeftSidebar.vue +19 -5
  34. package/src/Main.vue +9 -2
  35. package/src/RightSidebar.vue +12 -3
  36. package/src/SuspendCommon.vue +52 -38
  37. package/src/author/AuthorFans.vue +7 -5
  38. package/src/author/AuthorFollow.vue +14 -7
  39. package/src/author/AuthorGift.vue +25 -17
  40. package/src/author/AuthorInfo.vue +46 -19
  41. package/src/author/AuthorLink.vue +3 -1
  42. package/src/author/AuthorMedals.vue +5 -2
  43. package/src/author/AuthorPosts.vue +31 -13
  44. package/src/author/AuthorRss.vue +7 -3
  45. package/src/author/AuthorTeams.vue +3 -1
  46. package/src/author/UserPop.vue +29 -17
  47. package/src/bread/Admin.vue +55 -32
  48. package/src/bread/AdminDirectMessage.vue +15 -8
  49. package/src/bread/AdminDrop.vue +124 -45
  50. package/src/bread/CommunityAdmin.vue +527 -0
  51. package/src/bread/DesignTask.vue +30 -26
  52. package/src/bread/ListAdmin.vue +199 -0
  53. package/src/bread/MigrateCommunity.vue +192 -0
  54. package/src/bread/MoveToCommunityDialog.vue +234 -0
  55. package/src/comment/Avatar.vue +18 -24
  56. package/src/comment/CommentContent.vue +57 -37
  57. package/src/comment/CommentContentSimple.vue +61 -63
  58. package/src/comment/CommentInputForm.vue +45 -34
  59. package/src/comment/CommentWithReply.vue +9 -6
  60. package/src/comment/QuickReply.vue +5 -1
  61. package/src/comment/ReplyForReply.vue +6 -4
  62. package/src/comment/ReplyList.vue +28 -18
  63. package/src/comment/Upload.vue +24 -6
  64. package/src/filters/clientBy.vue +10 -8
  65. package/src/filters/markBy.vue +35 -31
  66. package/src/filters/menuBy.vue +20 -22
  67. package/src/filters/orderBy.vue +17 -11
  68. package/src/filters/tagBy.vue +4 -2
  69. package/src/filters/topicBy.vue +12 -10
  70. package/src/filters/versionBy.vue +5 -3
  71. package/src/filters/zlpBy.vue +29 -25
  72. package/src/footer/copyright.vue +5 -5
  73. package/src/footer/links.vue +2 -2
  74. package/src/footer/official.vue +1 -1
  75. package/src/footer/resource.vue +6 -6
  76. package/src/header/alternate.vue +35 -18
  77. package/src/header/asset.vue +9 -19
  78. package/src/header/box.vue +20 -8
  79. package/src/header/box2.vue +16 -9
  80. package/src/header/client.vue +7 -7
  81. package/src/header/lang.vue +41 -6
  82. package/src/header/logo.vue +2 -3
  83. package/src/header/manage.vue +2 -2
  84. package/src/header/message.vue +3 -3
  85. package/src/header/nav.vue +26 -23
  86. package/src/header/publish.vue +2 -2
  87. package/src/header/search.vue +2 -2
  88. package/src/header/shop.vue +2 -2
  89. package/src/header/user.vue +4 -4
  90. package/src/header/userInfo.vue +22 -24
  91. package/src/header/vip.vue +2 -2
  92. package/src/interact/BoxcoinAdmin.vue +30 -16
  93. package/src/interact/BoxcoinRecords.vue +28 -11
  94. package/src/interact/BoxcoinUser.vue +33 -17
  95. package/src/interact/Fav.vue +5 -1
  96. package/src/interact/Homework.vue +318 -0
  97. package/src/interact/Like.vue +4 -2
  98. package/src/interact/Rss.vue +10 -4
  99. package/src/interact/Share.vue +18 -19
  100. package/src/interact/WatchLater.vue +5 -1
  101. package/src/main.js +1 -0
  102. package/src/single/Author.vue +5 -1
  103. package/src/single/CmsSingle.vue +29 -15
  104. package/src/single/Collection.vue +36 -26
  105. package/src/single/Comment.vue +19 -13
  106. package/src/single/Creators.vue +5 -3
  107. package/src/single/PostCollection.vue +158 -0
  108. package/src/single/PostDirectory.vue +0 -126
  109. package/src/single/PostGuide.vue +93 -0
  110. package/src/single/PostHeader.vue +39 -17
  111. package/src/single/PostTopic.vue +3 -3
  112. package/src/single/PostVersion.vue +219 -0
  113. package/src/single/RightAffix.vue +160 -0
  114. package/src/single/SimpleThx.vue +4 -2
  115. package/src/single/Thx.vue +14 -2
  116. package/src/single/VersionDialog.vue +238 -0
  117. package/src/stories/components/StoryPropsTable.vue +137 -0
  118. package/src/stories/filters/FilterGallery.stories.js +110 -0
  119. package/src/stories/filters/clientBy.stories.js +24 -0
  120. package/src/stories/filters/markBy.stories.js +6 -0
  121. package/src/stories/filters/menuBy.stories.js +6 -0
  122. package/src/stories/filters/orderBy.stories.js +6 -0
  123. package/src/stories/filters/tagBy.stories.js +6 -0
  124. package/src/stories/filters/topicBy.stories.js +6 -0
  125. package/src/stories/filters/versionBy.stories.js +6 -0
  126. package/src/stories/filters/zlpBy.stories.js +6 -0
  127. package/src/stories/layout/BaseShell.stories.js +38 -0
  128. package/src/stories/layout/Breadcrumb.stories.js +40 -0
  129. package/src/stories/layout/CommonFooter.stories.js +1 -1
  130. package/src/stories/layout/CommonHeader.stories.js +1 -1
  131. package/src/stories/layout/LeftSideToggle.stories.js +78 -0
  132. package/src/stories/layout/LeftSidebar.stories.js +40 -0
  133. package/src/stories/layout/Main.stories.js +41 -0
  134. package/src/stories/layout/RightSidebar.stories.js +36 -0
  135. package/src/stories/layout/StorybookShell.vue +271 -0
  136. package/src/stories/layout/SuspendCommon.stories.js +67 -0
  137. package/src/stories/layout/SuspendCommonStage.vue +143 -0
  138. package/src/stories/mockData.js +275 -0
  139. package/src/stories/other/UserPop.stories.js +51 -0
  140. package/src/stories/single/Author.stories.js +19 -0
  141. package/src/stories/single/AuthorStoryStage.vue +103 -0
  142. package/src/stories/single/CmsSingle.stories.js +19 -0
  143. package/src/stories/single/CmsSingleStoryStage.vue +118 -0
  144. package/src/stories/single/Collection.stories.js +34 -0
  145. package/src/stories/single/Comment.stories.js +36 -0
  146. package/src/stories/single/Creators.stories.js +34 -0
  147. package/src/stories/single/PostCollection.stories.js +34 -0
  148. package/src/stories/single/PostDirectory.stories.js +31 -0
  149. package/src/stories/single/PostGuide.stories.js +47 -0
  150. package/src/stories/single/PostHeader.stories.js +39 -0
  151. package/src/stories/single/PostVersion.stories.js +41 -0
  152. package/src/stories/single/RightAffix.stories.js +84 -0
  153. package/src/stories/single/SimpleThx.stories.js +39 -0
  154. package/src/stories/single/SingleGallery.stories.js +207 -0
  155. package/src/stories/single/Thx.stories.js +38 -0
  156. package/vue.config.js +2 -12
  157. package/assets/css/common/bottom.less +0 -25
  158. package/assets/css/editor/article.less +0 -36
  159. package/assets/css/editor/combo.less +0 -362
  160. package/assets/css/editor/resource.less +0 -399
  161. package/assets/css/editor/tinymce.less +0 -100
  162. package/assets/css/editor/upload.less +0 -136
  163. package/assets/css/module/author.less +0 -231
  164. package/assets/css/module/buff.less +0 -88
  165. package/assets/css/module/directory.less +0 -165
  166. package/assets/css/module/icon.less +0 -25
  167. package/assets/css/module/item.less +0 -279
  168. package/assets/css/module/item_simple.less +0 -58
  169. package/assets/css/module/jx3_element.less +0 -5
  170. package/assets/css/module/macro.less +0 -89
  171. package/assets/css/module/npc.less +0 -265
  172. package/assets/css/module/resource.less +0 -93
  173. package/assets/css/module/skill.less +0 -74
  174. package/assets/css/module/talent.less +0 -495
  175. package/assets/js/a.js +0 -46
  176. package/assets/js/code.js +0 -13
  177. package/assets/js/combo.js +0 -40
  178. package/assets/js/directory.js +0 -114
  179. package/assets/js/filter2.js +0 -186
  180. package/assets/js/fold.js +0 -11
  181. package/assets/js/gallery.js +0 -14
  182. package/assets/js/hljs_languages.js +0 -177
  183. package/assets/js/iframe.js +0 -38
  184. package/assets/js/img.js +0 -23
  185. package/assets/js/item/attribute_percent.js +0 -88
  186. package/assets/js/item/bind.js +0 -18
  187. package/assets/js/item/border.js +0 -19
  188. package/assets/js/item/border_quest.js +0 -12
  189. package/assets/js/item/color.js +0 -25
  190. package/assets/js/item/hljs_languages.js +0 -177
  191. package/assets/js/item/icon_url.js +0 -15
  192. package/assets/js/item/second_format.js +0 -19
  193. package/assets/js/jx3_element.js +0 -73
  194. package/assets/js/katex.js +0 -181
  195. package/assets/js/macro.js +0 -16
  196. package/assets/js/nextpage.js +0 -4
  197. package/assets/js/pswp.js +0 -67
  198. package/assets/js/pswp_template.js +0 -66
  199. package/assets/js/pz_iframe.js +0 -30
  200. package/assets/js/qixue.js +0 -28
  201. package/assets/js/renderImgPreview.js +0 -18
  202. package/assets/js/script.js +0 -6
  203. package/assets/js/stat.js +0 -107
  204. package/assets/js/talent2.js +0 -21
  205. package/assets/js/tex-mml-chtml.js +0 -36310
  206. package/assets/js/voice.js +0 -238
  207. package/assets/js/xss.js +0 -130
  208. package/src/editor/Article.vue +0 -230
  209. package/src/editor/BoxResource.vue +0 -441
  210. package/src/editor/Resource.vue +0 -623
  211. package/src/editor/Tinymce.vue +0 -204
  212. package/src/editor/Upload.vue +0 -277
  213. package/src/editor/UploadAlum.vue +0 -176
  214. package/src/editor/UploadBanner.vue +0 -156
  215. package/src/editor/components/Author.vue +0 -322
  216. package/src/editor/components/Avatar.vue +0 -95
  217. package/src/editor/components/Buff.vue +0 -80
  218. package/src/editor/components/Combo.vue +0 -259
  219. package/src/editor/components/Equip.vue +0 -299
  220. package/src/editor/components/GameText.vue +0 -242
  221. package/src/editor/components/Item.vue +0 -325
  222. package/src/editor/components/ItemSimple.vue +0 -134
  223. package/src/editor/components/Medal.vue +0 -43
  224. package/src/editor/components/Npc.vue +0 -215
  225. package/src/editor/components/PostAuthor.vue +0 -102
  226. package/src/editor/components/Skill.vue +0 -71
@@ -0,0 +1,199 @@
1
+ <template>
2
+ <div v-if="hasRight" class="u-admin-button">
3
+ <el-button
4
+ type="primary"
5
+ icon="Setting"
6
+ class="c-admin-button u-admin"
7
+ :size="buttonSize"
8
+ @click="show = true"
9
+ >管理</el-button
10
+ >
11
+ <el-drawer
12
+ class="c-admin"
13
+ title="管理主题"
14
+ v-model:visible="show"
15
+ :before-close="close"
16
+ :append-to-body="true"
17
+ :modal="false"
18
+ :withHeader="false"
19
+ >
20
+ <div class="c-admin-wrapper" v-loading="loading">
21
+ <el-divider content-position="left">主题管理</el-divider>
22
+ <div class="m-bucket-list" v-if="list && list.length">
23
+ <div v-for="item in list" :key="item.id" class="m-bucket-item">
24
+ <el-tag size="medium" :type="item.status ? '' : 'info'">{{ item.name }}</el-tag>
25
+ <div class="m-bucket-op">
26
+ <el-button :type="item.status ? 'warning' : 'success'" plain size="mini" class="u-op-btn" :icon="item.status ? 'Download' : 'Upload'" @click="update(item.id, item.status)">{{ item.status ? '下架' : '上架' }}</el-button>
27
+ <el-button @click="edit(item)" plain size="mini" icon="Edit">修改</el-button>
28
+ <el-button type="info" plain size="mini" class="u-op-btn" icon="Delete" @click="del(item.id)">删除</el-button>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ <el-empty v-else description="暂无主题"></el-empty>
33
+
34
+ <div class="c-admin-buttons">
35
+ <el-button type="success" @click="add" icon="Plus">新增</el-button>
36
+ </div>
37
+ </div>
38
+ </el-drawer>
39
+ </div>
40
+ </template>
41
+
42
+ <script>
43
+ import { addTopicBucket, deleteTopicBucket, updateTopicBucket, getTopicBucket } from "../../service/admin";
44
+ import User from "@jx3box/jx3box-common/js/user";
45
+ export default {
46
+ name: "ListAdmin",
47
+ props: {
48
+ type: {
49
+ type: String,
50
+ default: "bbs",
51
+ },
52
+ buttonSize: {
53
+ type: String,
54
+ default: "medium",
55
+ },
56
+ },
57
+ data() {
58
+ return {
59
+ show: false,
60
+ list: [],
61
+ loading: false,
62
+ currentStatus: "",
63
+ };
64
+ },
65
+ computed: {
66
+ hasRight() {
67
+ return User.isEditor();
68
+ },
69
+ },
70
+ watch: {
71
+ show(val) {
72
+ val && this.loadData();
73
+ },
74
+ },
75
+ methods: {
76
+ close() {
77
+ this.show = false;
78
+ },
79
+ loadData() {
80
+ const params = {
81
+ type: this.type,
82
+ };
83
+ this.loading = true;
84
+ getTopicBucket(params)
85
+ .then((res) => {
86
+ this.list = res.data.data;
87
+ })
88
+ .finally(() => {
89
+ this.loading = false;
90
+ });
91
+ },
92
+ del(id) {
93
+ this.$confirm("此操作将删除该主题, 是否继续?", "提示", {
94
+ confirmButtonText: "确定",
95
+ cancelButtonText: "取消",
96
+ type: "warning",
97
+ })
98
+ .then(() => {
99
+ deleteTopicBucket(id).then((res) => {
100
+ this.$message({
101
+ type: "success",
102
+ message: "删除成功!",
103
+ });
104
+ this.loadData();
105
+ });
106
+ })
107
+ .catch(() => {});
108
+ },
109
+ update(id, status) {
110
+ const params = {
111
+ status: status ? 0 : 1,
112
+ };
113
+ updateTopicBucket(id, params).then((res) => {
114
+ this.$message({
115
+ type: "success",
116
+ message: "更新成功!",
117
+ });
118
+ this.loadData();
119
+ });
120
+ },
121
+ add() {
122
+ this.$prompt("请输入主题名称", "新增主题", {
123
+ confirmButtonText: "确定",
124
+ cancelButtonText: "取消",
125
+ inputPattern: /\S+/,
126
+ inputErrorMessage: "主题名称不能为空",
127
+ })
128
+ .then(({ value }) => {
129
+ const data = {
130
+ type: this.type,
131
+ name: value,
132
+ };
133
+ addTopicBucket(data).then((res) => {
134
+ this.$message({
135
+ type: "success",
136
+ message: "添加成功!",
137
+ });
138
+ this.loadData();
139
+ });
140
+ })
141
+ .catch(() => {});
142
+ },
143
+ edit(item) {
144
+ this.$prompt("请输入主题名称", "修改主题", {
145
+ confirmButtonText: "确定",
146
+ cancelButtonText: "取消",
147
+ inputPattern: /\S+/,
148
+ inputErrorMessage: "主题名称不能为空",
149
+ inputValue: item.name,
150
+ })
151
+ .then(({ value }) => {
152
+ const data = {
153
+ name: value,
154
+ };
155
+ updateTopicBucket(item.id, data).then((res) => {
156
+ this.$message({
157
+ type: "success",
158
+ message: "修改成功!",
159
+ });
160
+ this.loadData();
161
+ });
162
+ })
163
+ .catch(() => {})
164
+ }
165
+ },
166
+ };
167
+ </script>
168
+
169
+ <style lang="less">
170
+ .u-admin-button {
171
+ padding: 0;
172
+ float: right;
173
+ }
174
+ .c-admin-wrapper {
175
+ .u-admin-setting {
176
+ .pointer;
177
+ }
178
+ .m-bucket-list {
179
+ max-height: 300px;
180
+ overflow: auto;
181
+ .m-bucket-item {
182
+ .flex;
183
+ align-items: center;
184
+ justify-content: space-between;
185
+ padding: 5px;
186
+ border-radius: 3px;
187
+
188
+ // odd
189
+ &:nth-child(odd) {
190
+ background-color: #f7f7f7;
191
+ }
192
+ }
193
+ }
194
+ .u-bucket-add {
195
+ margin-top: 20px;
196
+ width: 100%;
197
+ }
198
+ }
199
+ </style>
@@ -0,0 +1,192 @@
1
+ <template>
2
+ <el-dialog
3
+ custom-class="w-dialog m-migrate-dialog"
4
+ :width="isPhone ? '95%' : '600px'"
5
+ v-model="visible"
6
+ @close="close"
7
+ title="迁回原板块"
8
+ append-to-body
9
+ draggable
10
+ >
11
+ <div class="m-migrate-box" v-if="post?.ID">
12
+ <div class="m-item">
13
+ <div class="u-label">标题</div>
14
+ <div class="u-value">{{ post?.post_title }}</div>
15
+ </div>
16
+ <div class="m-item">
17
+ <div class="u-label">作者</div>
18
+ <div class="u-value">
19
+ <a target="_blank" :href="authorLink(post?.post_author)">{{ `${post?.author}` }}</a>
20
+ </div>
21
+ </div>
22
+ <div class="m-item">
23
+ <div class="u-label">原板块</div>
24
+ <div class="u-value">{{ showType(post?.post_type) }}</div>
25
+ </div>
26
+ <div class="m-item">
27
+ <div class="u-label">原链接</div>
28
+ <div class="u-value"><a target="_blank" :href="postLink(post?.post_type, post?.ID)">查看</a></div>
29
+ </div>
30
+ </div>
31
+ <el-alert type="warning" title="该帖子不可迁移,不存在原帖子" :closable="false" show-icon v-else></el-alert>
32
+ <template #footer v-if="post?.ID">
33
+ <el-button @click="close" :loading="loading">关闭</el-button>
34
+ <el-button type="primary" @click="submit" :loading="loading">确认</el-button>
35
+ </template>
36
+ </el-dialog>
37
+ </template>
38
+
39
+ <script>
40
+ import { getOriginalPost, migrateCommunityPost } from "../../service/community";
41
+ import { postLink, authorLink } from "@jx3box/jx3box-common/js/utils";
42
+ import JX3BOX from "@jx3box/jx3box-common/data/jx3box.json";
43
+ const { __postType } = JX3BOX;
44
+ export default {
45
+ name: "MigrateCommunity",
46
+ props: {
47
+ modelValue: {
48
+ type: Boolean,
49
+ default: false,
50
+ },
51
+ communityId: {
52
+ type: Number,
53
+ default: 0,
54
+ },
55
+ },
56
+ emits: ["update:modelValue"],
57
+ data() {
58
+ return {
59
+ post: {},
60
+ isPhone: window.innerWidth < 768,
61
+
62
+ loading: false,
63
+
64
+ visible: false,
65
+ };
66
+ },
67
+ watch: {
68
+ modelValue(val) {
69
+ this.visible = val;
70
+ if (val) {
71
+ if (this.communityId) {
72
+ this.loadPost();
73
+ }
74
+ }
75
+ },
76
+ visible(val) {
77
+ this.$emit("update:modelValue", val);
78
+ }
79
+ },
80
+ methods: {
81
+ postLink,
82
+ authorLink,
83
+ close() {
84
+ this.$emit("update:modelValue", false);
85
+ },
86
+ loadPost() {
87
+ getOriginalPost(this.communityId).then((res) => {
88
+ this.post = res.data.data;
89
+ });
90
+ },
91
+ submit() {
92
+ if (!this.post) {
93
+ return;
94
+ }
95
+ this.$confirm("确认将该帖子迁回原板块?")
96
+ .then(() => {
97
+ this.loading = true;
98
+ migrateCommunityPost({ community_id: this.communityId })
99
+ .then((res) => {
100
+ this.$message.success("迁移成功");
101
+ this.close();
102
+ location.href = "/community";
103
+ })
104
+ .finally(() => {
105
+ this.loading = false;
106
+ });
107
+ })
108
+ .catch(() => {
109
+ // 取消操作
110
+ });
111
+ },
112
+ showType(type) {
113
+ return __postType[type] || type;
114
+ },
115
+ },
116
+ };
117
+ </script>
118
+
119
+ <style lang="less">
120
+ .m-migrate-dialog {
121
+ .el-dialog__body {
122
+ padding-top: 20px;
123
+ }
124
+ }
125
+
126
+ // 参考 Element UI Descriptions 样式
127
+ .m-migrate-box {
128
+ border: 1px solid #ebeef5;
129
+ border-radius: 4px;
130
+ background-color: #fff;
131
+
132
+ .m-item {
133
+ display: flex;
134
+ border-bottom: 1px solid #ebeef5;
135
+
136
+ &:last-child {
137
+ border-bottom: none;
138
+ }
139
+
140
+ .u-label {
141
+ width: 60px;
142
+ min-width: 60px;
143
+ padding: 12px 16px;
144
+ font-weight: bold;
145
+ color: #303133;
146
+ background-color: #fafafa;
147
+ border-right: 1px solid #ebeef5;
148
+ display: flex;
149
+ align-items: center;
150
+ font-size: 14px;
151
+ }
152
+
153
+ .u-value {
154
+ flex: 1;
155
+ padding: 12px 16px;
156
+ color: #606266;
157
+ word-break: break-all;
158
+ display: flex;
159
+ align-items: center;
160
+ font-size: 14px;
161
+ line-height: 1.6;
162
+
163
+ a {
164
+ color: #409eff;
165
+ text-decoration: none;
166
+ transition: color 0.3s;
167
+
168
+ &:hover {
169
+ color: #66b1ff;
170
+ }
171
+ }
172
+ }
173
+ }
174
+
175
+ // 响应式处理
176
+ @media screen and (max-width: 768px) {
177
+ .m-item {
178
+ flex-direction: column;
179
+
180
+ .u-label {
181
+ width: 100%;
182
+ border-right: none;
183
+ border-bottom: 1px solid #ebeef5;
184
+ }
185
+
186
+ .u-value {
187
+ width: 100%;
188
+ }
189
+ }
190
+ }
191
+ }
192
+ </style>
@@ -0,0 +1,234 @@
1
+ <template>
2
+ <el-dialog
3
+ custom-class="m-design-task"
4
+ :width="isPhone ? '95%' : '600px'"
5
+ v-model="visible"
6
+ @close="close"
7
+ title="迁移至论坛"
8
+ append-to-body
9
+ >
10
+ <el-form :model="form" ref="form" :rules="rules" :label-position="isPhone ? 'top' : 'left'" label-width="80px">
11
+ <el-form-item label="分类" prop="category">
12
+ <el-select v-model="form.category" placeholder="请选择帖子分类" style="width: 100%" filterable>
13
+ <el-option
14
+ v-for="item in categoryList"
15
+ :key="item.id"
16
+ :label="item.name"
17
+ :value="item.name"
18
+ ></el-option>
19
+ </el-select>
20
+ </el-form-item>
21
+ <el-form-item label="简介">
22
+ <el-input type="textarea" :rows="5" placeholder="请输入内容" v-model="form.introduction"> </el-input>
23
+ </el-form-item>
24
+ </el-form>
25
+
26
+ <el-divider content-position="left">附图</el-divider>
27
+ <div class="u-imgs">
28
+ <div :class="`u-imgs-item`" v-for="(item, i) in form.extra_images" :key="i">
29
+ <el-image :src="item" fit="cover" style="width: 148px; height: 148px" />
30
+ </div>
31
+ </div>
32
+
33
+ <el-divider content-position="left"></el-divider>
34
+
35
+ <div class="u_r_box">
36
+ <el-popover placement="top" trigger="manual" size="mini" content="请确认此操作不可逆" v-model="visible">
37
+ <template #reference>
38
+ <el-checkbox v-model="checked">我已确认此操作不可逆</el-checkbox>
39
+ </template>
40
+ </el-popover>
41
+ </div>
42
+ <template #footer>
43
+ <el-button @click="close">取 消</el-button>
44
+ <el-button type="primary" @click="onConfirm" :disabled="!checked">确 定</el-button>
45
+ </template>
46
+ </el-dialog>
47
+ </template>
48
+
49
+ <script>
50
+ import { getTopicBucket, recoverTopicFromPosts } from "../../service/community";
51
+
52
+ export default {
53
+ name: "MoveToCommunityDialog",
54
+ props: {
55
+ modelValue: {
56
+ type: Boolean,
57
+ default: false,
58
+ },
59
+ post: {
60
+ type: [Object, String],
61
+ default: () => {
62
+ return {};
63
+ },
64
+ },
65
+ },
66
+ emits: ["update:modelValue"],
67
+ data() {
68
+ return {
69
+ visible: false,
70
+ checked: false,
71
+ form: {
72
+ category: "",
73
+ id: "",
74
+ introduction: "",
75
+ extra_images: undefined,
76
+ },
77
+ categoryList: [],
78
+ isPhone: window.innerWidth < 768,
79
+ rules: {
80
+ category: [{ required: true, message: "请选择分类", trigger: "blur" }],
81
+ },
82
+ };
83
+ },
84
+ watch: {
85
+ modelValue(val) {
86
+ this.visible = val;
87
+ if (val) {
88
+ this.getCategoryList();
89
+ this.initForm();
90
+ }
91
+ },
92
+ },
93
+ methods: {
94
+ initForm() {
95
+ this.form.id = this.post.ID;
96
+ const content = this.post.post_content;
97
+ this.form.introduction = this.getIntroduction(content);
98
+ const imgs = this.getImgSrc(content);
99
+ if (imgs.length > 0) this.form.extra_images = [...new Set(imgs)];
100
+ },
101
+
102
+ close() {
103
+ this.$emit("update:modelValue", false);
104
+ },
105
+ clearForm() {
106
+ this.form = { category: "", id: "", introduction: "", extra_images: [] };
107
+ },
108
+ onConfirm() {
109
+ if (!this.post?.ID) {
110
+ this.$message.error("文章ID不存在!");
111
+ return;
112
+ }
113
+ if (!this.form.category) {
114
+ this.$message.error("请选择分类!");
115
+ return;
116
+ }
117
+ if (!this.checked) {
118
+ this.visible = true;
119
+ return;
120
+ }
121
+
122
+ this.$refs.form?.validate((valid) => {
123
+ if (valid) {
124
+ recoverTopicFromPosts(this.form).then(() => {
125
+ this.$message.success("操作成功");
126
+ this.close();
127
+ this.clearForm();
128
+ });
129
+ }
130
+ });
131
+ },
132
+ onCancel() {
133
+ this.close();
134
+ this.clearForm();
135
+ },
136
+ getCategoryList() {
137
+ getTopicBucket({ type: "community" }).then((res) => {
138
+ this.categoryList = res.data.data;
139
+ });
140
+ },
141
+ getIntroduction(str) {
142
+ // 使用正则表达式匹配HTML标签并将其替换为空字符串
143
+ const withoutTags = str?.replace(/<[^>]*>|\n|&nbsp;| &nbsp;/g, "");
144
+
145
+ // 获取前100个字符,如果字符串长度小于200,则获取全部字符
146
+ return withoutTags?.slice(0, 200);
147
+ },
148
+ getImgSrc: function (htmlString) {
149
+ // 创建一个正则表达式来匹配没有class属性的<img>标签,并且捕获src属性的值
150
+ const imgSrcRegex = /<img\s+(?![^>]*\bclass\b)[^>]*src="([^"]*)"/g;
151
+ let matches;
152
+ const imgSrcs = [];
153
+
154
+ // 使用正则表达式全局匹配HTML字符串中的所有<img>标签
155
+ while ((matches = imgSrcRegex.exec(htmlString)) !== null) {
156
+ // matches[1] 是正则表达式中捕获组的内容,即src的值
157
+ imgSrcs.push(matches[1]);
158
+ }
159
+
160
+ return imgSrcs;
161
+ },
162
+ },
163
+ };
164
+ </script>
165
+
166
+ <style lang="less">
167
+ .u_r_box {
168
+ display: flex;
169
+ justify-content: flex-end;
170
+ }
171
+ .u-imgs {
172
+ display: flex;
173
+ overflow-x: auto;
174
+ gap: 8px;
175
+ }
176
+ .u-imgs-item {
177
+ min-width: 148px;
178
+ overflow: hidden;
179
+ border-radius: 6px;
180
+ box-sizing: border-box;
181
+ height: 148px;
182
+ cursor: pointer;
183
+ position: relative;
184
+ border: 2px solid transparent;
185
+ transition: 0.35s;
186
+ background-color: @bg-light;
187
+ &:hover {
188
+ border-color: #0366d6;
189
+ }
190
+ &.active {
191
+ border-color: #0366d6;
192
+ .u-mark {
193
+ display: block;
194
+ }
195
+ }
196
+ img {
197
+ width: 100%;
198
+ }
199
+ .u-mark {
200
+ display: none;
201
+ position: absolute;
202
+ top: 2px;
203
+ right: 2px;
204
+ padding: 4px 8px;
205
+ font-size: 12px;
206
+ background-color: #0366d6;
207
+ color: white;
208
+ border-radius: 4px;
209
+ }
210
+ }
211
+ .m-design-task {
212
+ .el-form-item {
213
+ margin-bottom: 12px;
214
+ }
215
+ .m-star-line {
216
+ .el-form-item__content {
217
+ top: 10px;
218
+ }
219
+ }
220
+ .u-time {
221
+ color: #c0c4cc;
222
+ }
223
+ }
224
+
225
+ @media screen and (max-width: @phone) {
226
+ .m-design-task {
227
+ .m-star-line {
228
+ .el-form-item__content {
229
+ top: 0;
230
+ }
231
+ }
232
+ }
233
+ }
234
+ </style>
@@ -3,22 +3,16 @@
3
3
  <a class="u-avatar" :href="userHref" target="_blank">
4
4
  <el-avatar
5
5
  class="u-avatar-pic"
6
- :class="avatarSize==36?'xxs': ''"
6
+ :class="avatarSize == 36 ? 'xxs' : ''"
7
7
  shape="circle"
8
8
  :size="avatarSize"
9
9
  :src="userAvatar"
10
10
  ></el-avatar>
11
- <i class="u-avatar-frame" :class="avatarSize==36?'xxs':'xs'" v-if="avatarFrame">
11
+ <i class="u-avatar-frame" :class="avatarSize == 36 ? 'xxs' : 'xs'" v-if="avatarFrame">
12
12
  <img :src="frameUrl" />
13
13
  </i>
14
14
  </a>
15
- <el-link
16
- class="u-name"
17
- type="primary"
18
- target="_blank"
19
- :href="userHref"
20
- >{{ username }}</el-link
21
- >
15
+ <el-link class="u-name" type="primary" target="_blank" :href="userHref">{{ username }}</el-link>
22
16
  </div>
23
17
  </template>
24
18
 
@@ -54,16 +48,16 @@ export default {
54
48
  default: false,
55
49
  },
56
50
  },
57
- data: function() {
51
+ data: function () {
58
52
  return {};
59
53
  },
60
54
  computed: {
61
- frameUrl: function() {
55
+ frameUrl: function () {
62
56
  return __imgPath + `avatar/images/${this.avatarFrame}/${this.avatarFrame}.svg`;
63
57
  },
64
58
  },
65
59
  methods: {},
66
- mounted: function() {},
60
+ mounted: function () {},
67
61
  };
68
62
  </script>
69
63
 
@@ -100,23 +94,23 @@ export default {
100
94
  }
101
95
  .u-avatar-frame {
102
96
  position: absolute;
103
- &.xs{
97
+ &.xs {
104
98
  width: 68px;
105
99
  height: 68px;
106
- left:24px;
107
- top:24px;
108
- margin-left:-34px;
109
- margin-top:-34px;
100
+ left: 24px;
101
+ top: 24px;
102
+ margin-left: -34px;
103
+ margin-top: -34px;
110
104
  }
111
- &.xxs{
112
- @pic:36px;
113
- @frame:48px;
105
+ &.xxs {
106
+ @pic: 36px;
107
+ @frame: 48px;
114
108
  width: 48px;
115
109
  height: 48px;
116
- left:18px;
117
- top:18px;
118
- margin-left:-24px;
119
- margin-top:-24px;
110
+ left: 18px;
111
+ top: 18px;
112
+ margin-left: -24px;
113
+ margin-top: -24px;
120
114
  }
121
115
  img {
122
116
  width: 100%;