@jx3box/jx3box-common-ui 8.4.6 → 8.4.7

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.
package/package.json CHANGED
@@ -1,71 +1,71 @@
1
1
  {
2
- "name": "@jx3box/jx3box-common-ui",
3
- "version": "8.4.6",
4
- "description": "JX3BOX UI",
5
- "main": "index.js",
6
- "scripts": {
7
- "dev": "cross-env DEV_SERVER=true vue-cli-service serve",
8
- "serve": "vue-cli-service serve",
9
- "build": "vue-cli-service build",
10
- "lint": "vue-cli-service lint",
11
- "inspect": "vue inspect > output.js",
12
- "update": "npm --registry https://registry.npmjs.org install @jx3box/jx3box-common@latest @jx3box/jx3box-data@latest @jx3box/jx3box-comment-ui@latest @jx3box/jx3box-editor@latest",
13
- "header": "vue-cli-service build --target lib --name newheader src/Header.vue && cp public/index.html dist/newheader.html"
14
- },
15
- "eslintConfig": {
16
- "root": true,
17
- "env": {
18
- "node": true
2
+ "name": "@jx3box/jx3box-common-ui",
3
+ "version": "8.4.7",
4
+ "description": "JX3BOX UI",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "dev": "cross-env DEV_SERVER=true vue-cli-service serve",
8
+ "serve": "vue-cli-service serve",
9
+ "build": "vue-cli-service build",
10
+ "lint": "vue-cli-service lint",
11
+ "inspect": "vue inspect > output.js",
12
+ "update": "npm --registry https://registry.npmjs.org install @jx3box/jx3box-common@latest @jx3box/jx3box-data@latest @jx3box/jx3box-comment-ui@latest @jx3box/jx3box-editor@latest",
13
+ "header": "vue-cli-service build --target lib --name newheader src/Header.vue && cp public/index.html dist/newheader.html"
19
14
  },
20
- "extends": [
21
- "plugin:vue/essential"
15
+ "eslintConfig": {
16
+ "root": true,
17
+ "env": {
18
+ "node": true
19
+ },
20
+ "extends": [
21
+ "plugin:vue/essential"
22
+ ],
23
+ "rules": {},
24
+ "parserOptions": {
25
+ "parser": "babel-eslint"
26
+ }
27
+ },
28
+ "browserslist": [
29
+ "> 1%",
30
+ "last 2 versions"
22
31
  ],
23
- "rules": {},
24
- "parserOptions": {
25
- "parser": "babel-eslint"
32
+ "dependencies": {
33
+ "@jx3box/jx3box-comment-ui": "^1.8.7",
34
+ "@jx3box/jx3box-common": "^8.2.16",
35
+ "@jx3box/jx3box-data": "^3.5.8",
36
+ "@jx3box/jx3box-editor": "^2.1.9",
37
+ "@jx3box/reporter": "^0.0.4",
38
+ "axios": "^0.26.1",
39
+ "dayjs": "^1.11.0",
40
+ "element-ui": "^2.13.2",
41
+ "jquery": "^3.5.1",
42
+ "lodash": "^4.17.15",
43
+ "qrcode.vue": "^1.7.0",
44
+ "url": "^0.11.0",
45
+ "vue": "^2.6.11"
46
+ },
47
+ "devDependencies": {
48
+ "@babel/plugin-proposal-optional-chaining": "^7.14.5",
49
+ "@vue/cli-plugin-babel": "~4.3.0",
50
+ "@vue/cli-plugin-eslint": "~4.3.0",
51
+ "@vue/cli-plugin-vuex": "^4.0.0",
52
+ "@vue/cli-service": "~4.3.0",
53
+ "babel-eslint": "^10.1.0",
54
+ "core-js": "^3.6.5",
55
+ "cross-env": "^7.0.3",
56
+ "csslab": "^4.0.3",
57
+ "eslint": "^6.7.2",
58
+ "eslint-plugin-vue": "^6.2.2",
59
+ "less": "^3.0.4",
60
+ "less-loader": "^5.0.0",
61
+ "style-resources-loader": "^1.3.3",
62
+ "vue-cli-plugin-element": "~1.0.1",
63
+ "vue-svg-inline-loader": "^1.4.6",
64
+ "vue-template-compiler": "^2.6.11",
65
+ "weixin-js-sdk": "^1.6.0"
66
+ },
67
+ "repository": {
68
+ "type": "git",
69
+ "url": "git+https://github.com/JX3BOX/jx3box-common-ui.git"
26
70
  }
27
- },
28
- "browserslist": [
29
- "> 1%",
30
- "last 2 versions"
31
- ],
32
- "dependencies": {
33
- "@jx3box/jx3box-comment-ui": "^1.8.7",
34
- "@jx3box/jx3box-common": "^8.2.16",
35
- "@jx3box/jx3box-data": "^3.5.8",
36
- "@jx3box/jx3box-editor": "^2.1.9",
37
- "@jx3box/reporter": "^0.0.4",
38
- "axios": "^0.26.1",
39
- "dayjs": "^1.11.0",
40
- "element-ui": "^2.13.2",
41
- "jquery": "^3.5.1",
42
- "lodash": "^4.17.15",
43
- "qrcode.vue": "^1.7.0",
44
- "url": "^0.11.0",
45
- "vue": "^2.6.11"
46
- },
47
- "devDependencies": {
48
- "@babel/plugin-proposal-optional-chaining": "^7.14.5",
49
- "@vue/cli-plugin-babel": "~4.3.0",
50
- "@vue/cli-plugin-eslint": "~4.3.0",
51
- "@vue/cli-plugin-vuex": "^4.0.0",
52
- "@vue/cli-service": "~4.3.0",
53
- "babel-eslint": "^10.1.0",
54
- "core-js": "^3.6.5",
55
- "cross-env": "^7.0.3",
56
- "csslab": "^4.0.3",
57
- "eslint": "^6.7.2",
58
- "eslint-plugin-vue": "^6.2.2",
59
- "less": "^3.0.4",
60
- "less-loader": "^5.0.0",
61
- "style-resources-loader": "^1.3.3",
62
- "vue-cli-plugin-element": "~1.0.1",
63
- "vue-svg-inline-loader": "^1.4.6",
64
- "vue-template-compiler": "^2.6.11",
65
- "weixin-js-sdk": "^1.6.0"
66
- },
67
- "repository": {
68
- "type": "git",
69
- "url": "git+https://github.com/JX3BOX/jx3box-common-ui.git"
70
- }
71
- }
71
+ }
@@ -0,0 +1,10 @@
1
+ import { $cms, $next } from "@jx3box/jx3box-common/js/https";
2
+ const API_PREFIX = `/api/next2`;
3
+ // 获取分类
4
+ export function getTopicBucket(params) {
5
+ return $cms().get(`/api/cms/topic/bucket`, { params });
6
+ }
7
+
8
+ export const recoverTopicFromPosts = (data) => {
9
+ return $next().post(`${API_PREFIX}/community/discussion/manage/topic/recover/from/posts`, data);
10
+ };
@@ -11,6 +11,9 @@
11
11
  <el-dropdown-item v-if="isEditor" command="directMessage" icon="el-icon-message">
12
12
  <span>私信</span>
13
13
  </el-dropdown-item>
14
+ <el-dropdown-item v-if="isEditor && showMove" command="onMoveToCommunity" icon="el-icon-upload">
15
+ <span>转移</span>
16
+ </el-dropdown-item>
14
17
  <el-dropdown-item icon="el-icon-upload" command="designTask" v-if="hasPermission('push_banner')">
15
18
  <span>推送</span>
16
19
  </el-dropdown-item>
@@ -18,6 +21,7 @@
18
21
  </el-dropdown>
19
22
 
20
23
  <design-task v-model="showDesignTask" :post="post"></design-task>
24
+ <MoveToCommunityDialog v-model="moveVisible" :post="post" />
21
25
  </div>
22
26
  </template>
23
27
 
@@ -25,13 +29,19 @@
25
29
  import Bus from "../../service/bus";
26
30
  import User from "@jx3box/jx3box-common/js/user";
27
31
  import DesignTask from "./DesignTask.vue";
32
+ import MoveToCommunityDialog from "./MoveToCommunityDialog.vue";
28
33
  import { sendMessage } from "../../service/admin";
29
34
  export default {
30
35
  name: "AdminDrop",
31
36
  components: {
32
- DesignTask
37
+ DesignTask,
38
+ MoveToCommunityDialog,
33
39
  },
34
40
  props: {
41
+ showMove: {
42
+ type: Boolean,
43
+ default: false,
44
+ },
35
45
  buttonSize: {
36
46
  type: String,
37
47
  default: "medium",
@@ -47,18 +57,19 @@ export default {
47
57
  },
48
58
  data() {
49
59
  return {
60
+ moveVisible: false,
50
61
  showDesignTask: false,
51
- }
62
+ };
52
63
  },
53
64
  computed: {
54
65
  isEditor() {
55
66
  return User.isEditor();
56
67
  },
57
68
  sourceId() {
58
- return this.post?.ID
69
+ return this.post?.ID;
59
70
  },
60
71
  sourceType() {
61
- return this.post?.post_type
72
+ return this.post?.post_type;
62
73
  },
63
74
  },
64
75
  methods: {
@@ -68,6 +79,9 @@ export default {
68
79
  toggleAdminPanel() {
69
80
  Bus.$emit("toggleAdminPanel");
70
81
  },
82
+ onMoveToCommunity() {
83
+ this.moveVisible = true;
84
+ },
71
85
  directMessage() {
72
86
  this.$prompt("请输入私信内容", "管理私信", {
73
87
  confirmButtonText: "确定",
@@ -86,25 +100,25 @@ export default {
86
100
  user_id: this.userId,
87
101
  content: "运营通知:" + instance.inputValue,
88
102
  type: "system",
89
- subtype: "admin_message"
103
+ subtype: "admin_message",
90
104
  };
91
105
  sendMessage(data).then(() => {
92
106
  this.$message.success("私信成功");
93
107
  done();
94
- })
108
+ });
95
109
  } else {
96
110
  done();
97
111
  }
98
- }
99
- }).catch(() => {})
112
+ },
113
+ }).catch(() => {});
100
114
  },
101
115
  designTask() {
102
116
  this.showDesignTask = true;
103
117
  },
104
118
  hasPermission(permission) {
105
119
  return User.hasPermission(permission);
106
- }
107
- }
120
+ },
121
+ },
108
122
  };
109
123
  </script>
110
124
 
@@ -0,0 +1,219 @@
1
+ <template>
2
+ <el-dialog
3
+ custom-class="m-design-task"
4
+ :width="isPhone ? '95%' : '600px'"
5
+ :visible="modelValue"
6
+ @close="close"
7
+ title="迁移至论坛"
8
+ append-to-body
9
+ >
10
+ <el-form :model="form" ref="form" :label-position="isPhone ? 'top' : 'left'" label-width="80px">
11
+ <el-form-item label="分类">
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
+
25
+ <el-divider content-position="left">附图</el-divider>
26
+ <div class="u-imgs">
27
+ <div
28
+ :class="`u-imgs-item ${form.banner_img === item && 'active'}`"
29
+ v-for="(item, i) in form.extra_images"
30
+ :key="i"
31
+ @click="setBannerIndex(item)"
32
+ >
33
+ <el-image :src="item" fit="cover" style="width: 148px; height: 148px" />
34
+ <div class="u-mark">封面</div>
35
+ </div>
36
+ </div>
37
+ </el-form>
38
+ <template #footer>
39
+ <el-button @click="close">取 消</el-button>
40
+ <el-button type="primary" @click="onConfirm">确 定</el-button>
41
+ </template>
42
+ </el-dialog>
43
+ </template>
44
+
45
+ <script>
46
+ import { getTopicBucket, recoverTopicFromPosts } from "../../service/community";
47
+
48
+ export default {
49
+ name: "MoveToCommunityDialog",
50
+ props: {
51
+ modelValue: {
52
+ type: Boolean,
53
+ default: false,
54
+ },
55
+ post: {
56
+ type: Object,
57
+ default: () => {
58
+ return {};
59
+ },
60
+ },
61
+ },
62
+ model: {
63
+ prop: "modelValue",
64
+ event: "update:modelValue",
65
+ },
66
+ emits: ["update:modelValue"],
67
+ data() {
68
+ return {
69
+ form: {
70
+ category: "",
71
+ id: "",
72
+ introduction: "",
73
+ banner_img: "",
74
+ extra_images: [],
75
+ },
76
+ categoryList: [],
77
+ isPhone: window.innerWidth < 768,
78
+ };
79
+ },
80
+ watch: {
81
+ modelValue(val) {
82
+ if (val) {
83
+ this.getCategoryList();
84
+ this.initForm();
85
+ }
86
+ },
87
+ },
88
+ methods: {
89
+ initForm() {
90
+ this.form.id = this.post.ID;
91
+ const content = this.post.post_content;
92
+ this.form.introduction = this.getIntroduction(content);
93
+ const imgs = this.getImgSrc(content);
94
+ this.form.extra_images = [...new Set(imgs)];
95
+ },
96
+
97
+ close() {
98
+ this.$emit("update:modelValue", false);
99
+ },
100
+ clearForm() {
101
+ this.form = { category: "", id: "", introduction: "", banner_img: "", extra_images: [] };
102
+ },
103
+ setBannerIndex(img) {
104
+ this.form.banner_img = img;
105
+ },
106
+ onConfirm() {
107
+ if (!this.post?.ID) {
108
+ this.$message.error("文章ID不存在!");
109
+ return;
110
+ }
111
+ if (!this.form.category) {
112
+ this.$message.error("请选择分类!");
113
+ return;
114
+ }
115
+ recoverTopicFromPosts(this.form).then(() => {
116
+ this.$message.success("操作成功");
117
+ this.close();
118
+ this.clearForm();
119
+ });
120
+ },
121
+ onCancel() {
122
+ this.close();
123
+ this.clearForm();
124
+ },
125
+ getCategoryList() {
126
+ getTopicBucket({ type: "community" }).then((res) => {
127
+ this.categoryList = res.data.data;
128
+ });
129
+ },
130
+ getIntroduction(str) {
131
+ // 使用正则表达式匹配HTML标签并将其替换为空字符串
132
+ const withoutTags = str.replace(/<[^>]*>|\n|&nbsp;| &nbsp;/g, "");
133
+
134
+ // 获取前100个字符,如果字符串长度小于200,则获取全部字符
135
+ return withoutTags.slice(0, 200);
136
+ },
137
+ getImgSrc: function (htmlString) {
138
+ // 创建一个正则表达式来匹配没有class属性的<img>标签,并且捕获src属性的值
139
+ const imgSrcRegex = /<img\s+(?![^>]*\bclass\b)[^>]*src="([^"]*)"/g;
140
+ let matches;
141
+ const imgSrcs = [];
142
+
143
+ // 使用正则表达式全局匹配HTML字符串中的所有<img>标签
144
+ while ((matches = imgSrcRegex.exec(htmlString)) !== null) {
145
+ // matches[1] 是正则表达式中捕获组的内容,即src的值
146
+ imgSrcs.push(matches[1]);
147
+ }
148
+
149
+ return imgSrcs;
150
+ },
151
+ },
152
+ };
153
+ </script>
154
+
155
+ <style lang="less">
156
+ .u-imgs {
157
+ display: flex;
158
+ overflow-x: auto;
159
+ gap: 8px;
160
+ }
161
+ .u-imgs-item {
162
+ min-width: 148px;
163
+ overflow: hidden;
164
+ border-radius: 6px;
165
+ box-sizing: border-box;
166
+ height: 148px;
167
+ cursor: pointer;
168
+ position: relative;
169
+ border: 2px solid transparent;
170
+ transition: 0.35s;
171
+ background-color: @bg-light;
172
+ &:hover {
173
+ border-color: #0366d6;
174
+ }
175
+ &.active {
176
+ border-color: #0366d6;
177
+ .u-mark {
178
+ display: block;
179
+ }
180
+ }
181
+ img {
182
+ width: 100%;
183
+ }
184
+ .u-mark {
185
+ display: none;
186
+ position: absolute;
187
+ top: 2px;
188
+ right: 2px;
189
+ padding: 4px 8px;
190
+ font-size: 12px;
191
+ background-color: #0366d6;
192
+ color: white;
193
+ border-radius: 4px;
194
+ }
195
+ }
196
+ .m-design-task {
197
+ .el-form-item {
198
+ margin-bottom: 12px;
199
+ }
200
+ .m-star-line {
201
+ .el-form-item__content {
202
+ top: 10px;
203
+ }
204
+ }
205
+ .u-time {
206
+ color: #c0c4cc;
207
+ }
208
+ }
209
+
210
+ @media screen and (max-width: @phone) {
211
+ .m-design-task {
212
+ .m-star-line {
213
+ .el-form-item__content {
214
+ top: 0;
215
+ }
216
+ }
217
+ }
218
+ }
219
+ </style>