hookehuyr

✨ feat(客户端): 书籍详情页完善相关功能

1 import { createApp } from 'vue'; 1 import { createApp } from 'vue';
2 -import { Button, Image as VanImage, Col, Row, Icon, Form, Field, CellGroup, ConfigProvider, Toast, Uploader, Empty, Tab, Tabs, Overlay, NumberKeyboard, Lazyload, List, PullRefresh } from 'vant'; 2 +import { Button, Image as VanImage, Col, Row, Icon, Form, Field, CellGroup, ConfigProvider, Toast, Uploader, Empty, Tab, Tabs, Overlay, NumberKeyboard, Lazyload, List, PullRefresh, Popup, Picker } from 'vant';
3 import router from './router'; 3 import router from './router';
4 import App from './App.vue'; 4 import App from './App.vue';
5 import axios from './utils/axios'; 5 import axios from './utils/axios';
...@@ -28,6 +28,8 @@ app.use(NumberKeyboard); ...@@ -28,6 +28,8 @@ app.use(NumberKeyboard);
28 app.use(Lazyload); 28 app.use(Lazyload);
29 app.use(List); 29 app.use(List);
30 app.use(PullRefresh); 30 app.use(PullRefresh);
31 +app.use(Popup);
32 +app.use(Picker);
31 33
32 app.use(ConfigProvider); 34 app.use(ConfigProvider);
33 35
......
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
3 <div class="modify-top"></div> 3 <div class="modify-top"></div>
4 <div class="book-detail"> 4 <div class="book-detail">
5 <div style="text-align: center;"> 5 <div style="text-align: center;">
6 - <van-image width="220" height="220" src="https://lanhu.oss-cn-beijing.aliyuncs.com/SketchPngc434046fdf1f9499d251b280af2568ddbe64839799d00a9aee226edbeb710aed" /> 6 + <van-image width="220" height="220"
7 + src="https://lanhu.oss-cn-beijing.aliyuncs.com/SketchPngc434046fdf1f9499d251b280af2568ddbe64839799d00a9aee226edbeb710aed" />
7 </div> 8 </div>
8 <div class="book-intro"> 9 <div class="book-intro">
9 <p class="book-post">逃家小兔绘本</p> 10 <p class="book-post">逃家小兔绘本</p>
...@@ -12,8 +13,12 @@ ...@@ -12,8 +13,12 @@
12 一场爱的捉迷藏就此展开了 13 一场爱的捉迷藏就此展开了
13 </div> 14 </div>
14 <div v-if="hasToggle"> 15 <div v-if="hasToggle">
15 - <div v-if="isToggle" @click="onToggle(false)" class="book-toggle-icon">展开&nbsp;<van-icon style="vertical-align: middle;" size="0.9rem" :name="icon_down" /></div> 16 + <div v-if="isToggle" @click="onToggle(false)" class="book-toggle-icon">展开&nbsp;
16 - <div v-else @click="onToggle(true)" class="book-toggle-icon">折叠&nbsp;<van-icon style="vertical-align: middle;" size="0.9rem" :name="icon_up" /></div> 17 + <van-icon style="vertical-align: middle;" size="0.9rem" :name="icon_down" />
18 + </div>
19 + <div v-else @click="onToggle(true)" class="book-toggle-icon">折叠&nbsp;
20 + <van-icon style="vertical-align: middle;" size="0.9rem" :name="icon_up" />
21 + </div>
17 </div> 22 </div>
18 </div> 23 </div>
19 24
...@@ -35,15 +40,15 @@ ...@@ -35,15 +40,15 @@
35 <div class="book-video-language"> 40 <div class="book-video-language">
36 <van-row> 41 <van-row>
37 <van-col span="6"> 42 <van-col span="6">
38 - <div class="uncheck">普通话</div> 43 + <div @click="toggleLanguage" :class="[check_mandarin ? 'checked' : 'uncheck']">普通话</div>
39 </van-col> 44 </van-col>
40 <van-col span="6"> 45 <van-col span="6">
41 - <div class="checked">方言</div> 46 + <div @click="toggleLanguage" :class="[check_localism ? 'checked' : 'uncheck']">方言</div>
42 </van-col> 47 </van-col>
43 - <van-col span="12"> 48 + <van-col span="12" v-if="check_localism" @click="showPicker = true">
44 <div class="choose-wrapper"> 49 <div class="choose-wrapper">
45 <div class="text"> 50 <div class="text">
46 - &nbsp;所有方言 51 + &nbsp;{{ chooseLanguage.text }}
47 </div> 52 </div>
48 <div class="icon"> 53 <div class="icon">
49 <van-icon name="arrow-down" />&nbsp; 54 <van-icon name="arrow-down" />&nbsp;
...@@ -51,29 +56,34 @@ ...@@ -51,29 +56,34 @@
51 </div> 56 </div>
52 </van-col> 57 </van-col>
53 </van-row> 58 </van-row>
59 + <van-popup v-model:show="showPicker" round position="bottom">
60 + <van-picker
61 + :columns="columns"
62 + :columns-field-names="{ text: 'text', value: 'val', children: 'children' }"
63 + @cancel="showPicker = false"
64 + @confirm="onConfirm"
65 + />
66 + </van-popup>
54 </div> 67 </div>
55 68
56 <div class="book-video-list"> 69 <div class="book-video-list">
57 - <van-list 70 + <van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
58 - v-model:loading="loading" 71 + <div v-for="item in list" :key="item" style="height: 3rem;">{{ item }}</div>
59 - :finished="finished"
60 - finished-text="没有更多了"
61 - @load="onLoad">
62 - <div v-for="item in list" :key="item" style="height: 3rem;" >{{ item }}</div>
63 </van-list> 72 </van-list>
64 </div> 73 </div>
65 </div> 74 </div>
66 <div style="height: 5rem;"></div> 75 <div style="height: 5rem;"></div>
67 <div class="book-bar"> 76 <div class="book-bar">
68 - <div class="text"> 77 + <div @click="onSubscribe" class="text">
69 - <van-icon :name="icon_subscribed" size="1.25rem" style="margin: 0 auto;" /> 78 + <van-icon v-if="!is_subscribe" :name="icon_subscribed" size="1.25rem" style="margin: 0 auto;" />
70 - <span style="font-size: 0.85rem;">订阅</span> 79 + <van-icon v-else :name="icon_unsubscribe" size="1.25rem" style="margin: 0 auto;" />
80 + <span style="font-size: 0.85rem;">订阅</span>
71 </div> 81 </div>
72 <div class="button"> 82 <div class="button">
73 - <my-button type="custom" :custom-style="styleObject1">爱心捐书</my-button> 83 + <my-button @on-click="payFor" type="custom" :custom-style="styleObject1">爱心捐书</my-button>
74 </div> 84 </div>
75 <div class="button"> 85 <div class="button">
76 - <my-button type="custom" :custom-style="styleObject2">上传作品</my-button> 86 + <my-button @on-click="uploadVideo" type="custom" :custom-style="styleObject2">上传作品</my-button>
77 </div> 87 </div>
78 </div> 88 </div>
79 <to-me @on-click="gotoMe()"></to-me> 89 <to-me @on-click="gotoMe()"></to-me>
...@@ -99,65 +109,112 @@ import { Toast } from 'vant'; ...@@ -99,65 +109,112 @@ import { Toast } from 'vant';
99 const $route = useRoute(); 109 const $route = useRoute();
100 const $router = useRouter(); 110 const $router = useRouter();
101 111
102 - // 自定义按钮颜色样式 112 +// 自定义按钮颜色样式
103 - const styleObject1 = reactive({ 113 +const styleObject1 = reactive({
104 - backgroundColor: '#FFFFFF', 114 + backgroundColor: '#FFFFFF',
105 - color: '#713610', 115 + color: '#713610',
106 - borderColor: '#713610' 116 + borderColor: '#713610'
107 - }) 117 +})
108 - const styleObject2 = reactive({ 118 +const styleObject2 = reactive({
109 - backgroundColor: '#F9D95C', 119 + backgroundColor: '#F9D95C',
110 - color: '#713610', 120 + color: '#713610',
111 - borderColor: '#F9D95C' 121 + borderColor: '#F9D95C'
112 - }) 122 +})
113 - 123 +
114 - const items = reactive([]) 124 +const items = reactive([])
115 - 125 +
116 - const gotoMe = () => { 126 +const gotoMe = () => {
117 - console.warn('跳转我的地址'); 127 + console.warn('跳转我的地址');
128 +}
129 +
130 +// 判断是否显示简介的展开图标
131 +const hasToggle = ref(false); // 判断是否有展开文字,默认没有
132 +const isToggle = ref(true); // 判断展开状态,默认展开
133 +
134 +const onToggle = (v) => { // 展开/折叠
135 + isToggle.value = v
136 +}
137 +
138 +// 切换视频语言
139 +const check_mandarin = ref(true);
140 +const check_localism = ref(false);
141 +const chooseLanguage = ref({ text: '普通话', val: '00' }); // 默认选中普通话
142 +const toggleLanguage = () => {
143 + check_mandarin.value = !check_mandarin.value;
144 + check_localism.value = !check_localism.value;
145 + // 修改默认语言绑定数据
146 + if (check_localism.value) {
147 + chooseLanguage.value = { text: columns[0]['text'], val: columns[0]['val'] }
148 + } else {
149 + chooseLanguage.value = { text: '普通话', val: '00' };
118 } 150 }
151 +}
152 +// 方言选择项
153 +const columns = [
154 + { text: '所有方言', val: '00' },
155 + { text: '沪语', val: '01' },
156 + { text: '粤语', val: '02' },
157 +];
119 158
120 - // 判断是否显示简介的展开图标 159 +const showPicker = ref(false);
121 - let hasToggle = ref(false); // 判断是否有展开文字,默认没有
122 - let isToggle = ref(true); // 判断展开状态,默认展开
123 160
124 - const onToggle = (v) => { // 展开/折叠 161 +const onConfirm = ({ selectedOptions }) => {
125 - isToggle.value = v 162 + showPicker.value = false;
163 + chooseLanguage.value = {
164 + text: selectedOptions[0].text,
165 + val: selectedOptions[0].val
126 } 166 }
167 +};
127 168
128 - onMounted(() => { 169 +onMounted(() => {
129 - // 判断是否显示简介的展开图标 170 + // 判断是否显示简介的展开图标
130 - hasToggle.value = tools.hasEllipsis('book-intro'); 171 + hasToggle.value = tools.hasEllipsis('book-intro');
131 - for (let index = 0; index < 20; index++) { 172 + for (let index = 0; index < 20; index++) {
132 - items.push({ 173 + items.push({
133 - id: index, 174 + id: index,
134 - avatar: 'https://cdn.jsdelivr.net/npm/@vant/assets/cat.jpeg' 175 + avatar: 'https://cdn.jsdelivr.net/npm/@vant/assets/cat.jpeg'
135 - }) 176 + })
177 + }
178 +})
179 +
180 +// 处理书籍下作品列表
181 +const list = ref([]);
182 +const loading = ref(false);
183 +const finished = ref(false);
184 +
185 +const onLoad = () => {
186 + // 异步更新数据
187 + // setTimeout 仅做示例,真实场景中一般为 ajax 请求
188 + setTimeout(() => {
189 + for (let i = 0; i < 20; i++) {
190 + list.value.push(list.value.length + 1);
136 } 191 }
137 - })
138 -
139 - // 处理书籍下作品列表
140 - const list = ref([]);
141 - const loading = ref(false);
142 - const finished = ref(false);
143 -
144 - const onLoad = () => {
145 - // 异步更新数据
146 - // setTimeout 仅做示例,真实场景中一般为 ajax 请求
147 - setTimeout(() => {
148 - for (let i = 0; i < 20; i++) {
149 - list.value.push(list.value.length + 1);
150 - }
151 192
152 - // 加载状态结束 193 + // 加载状态结束
153 - loading.value = false; 194 + loading.value = false;
154 195
155 - // 数据全部加载完成 196 + // 数据全部加载完成
156 - if (list.value.length >= 100) { 197 + if (list.value.length >= 100) {
157 - finished.value = true; 198 + finished.value = true;
158 - } 199 + }
159 - }, 1000); 200 + }, 1000);
160 - }; 201 +};
202 +
203 +// 书籍订阅
204 +let is_subscribe = ref(false);
205 +const onSubscribe = () => {
206 + is_subscribe.value = !is_subscribe.value
207 +}
208 +
209 +// 爱心捐书
210 +const payFor = () => {
211 + console.warn('弹出框');
212 +}
213 +
214 +// 上传作品
215 +const uploadVideo = () => {
216 + console.warn('跳转页面');
217 +}
161 </script> 218 </script>
162 219
163 <script> 220 <script>
...@@ -165,12 +222,12 @@ import mixin from 'common/mixin'; ...@@ -165,12 +222,12 @@ import mixin from 'common/mixin';
165 222
166 export default { 223 export default {
167 mixins: [mixin.init], 224 mixins: [mixin.init],
168 - data () { 225 + data() {
169 return { 226 return {
170 227
171 } 228 }
172 }, 229 },
173 - mounted () { 230 + mounted() {
174 231
175 }, 232 },
176 methods: { 233 methods: {
...@@ -181,103 +238,120 @@ export default { ...@@ -181,103 +238,120 @@ export default {
181 238
182 <style lang="less" scoped> 239 <style lang="less" scoped>
183 @import url('@css/content-bg.less'); 240 @import url('@css/content-bg.less');
184 - .book-detail-page { 241 +
185 - overflow: auto; 242 +.book-detail-page {
186 - .book-detail { 243 + overflow: auto;
187 - margin: 1rem; 244 +
188 - margin-top: 1.25rem; 245 + .book-detail {
189 - padding-top: 1rem; 246 + margin: 1rem;
190 - border-radius: 10px; 247 + margin-top: 1.25rem;
191 - background-color: rgba(255, 255, 255, 1); 248 + padding-top: 1rem;
192 - box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.13); 249 + border-radius: 10px;
193 - .book-intro { 250 + background-color: rgba(255, 255, 255, 1);
194 - padding: 1rem; 251 + box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.13);
195 - .book-post { 252 +
196 - color: #222222; 253 + .book-intro {
197 - font-size: 1.25rem; 254 + padding: 1rem;
198 - font-weight: bold; 255 +
199 - } 256 + .book-post {
200 - #book-intro { 257 + color: #222222;
201 - color: #333333; 258 + font-size: 1.25rem;
202 - margin-top: 0.25rem; 259 + font-weight: bold;
203 - }
204 - .book-toggle-icon {
205 - text-align: right;
206 - color: #713610;
207 - font-size: 1rem;
208 - }
209 - }
210 - .book-video-title {
211 - background-color: #F7F7F7;
212 - padding: 1rem 1.5rem;
213 } 260 }
214 - .book-video-language { 261 +
215 - padding: 1rem; 262 + #book-intro {
216 - .uncheck { 263 + color: #333333;
217 - background: #F7F7F7; 264 + margin-top: 0.25rem;
218 - border-radius: 15px;
219 - padding: 0.5rem;
220 - text-align: center;
221 - color: #222222;
222 - margin: 0 0.25rem;
223 - }
224 - .checked {
225 - background: #F9D95C;
226 - border-radius: 15px;
227 - padding: 0.5rem;
228 - text-align: center;
229 - color: #222222;
230 - margin: 0 0.25rem;
231 - }
232 - .choose-wrapper {
233 - background: #F7F7F7;
234 - border-radius: 15px;
235 - padding: 0.5rem;
236 - text-align: center;
237 - color: #B0B0B0;
238 - margin: 0 0.25rem;
239 - .text {
240 - display: inline-block;
241 - text-align: left;
242 - width: 80%;
243 - }
244 - .icon {
245 - display: inline-block;
246 - text-align: right;
247 - width: 20%;
248 - }
249 - }
250 } 265 }
251 - .book-video-list { 266 +
252 - height: 20rem; 267 + .book-toggle-icon {
253 - overflow: scroll; 268 + text-align: right;
269 + color: #713610;
270 + font-size: 1rem;
254 } 271 }
255 } 272 }
256 - .book-bar { 273 +
257 - position: fixed; 274 + .book-video-title {
258 - right: 0; 275 + background-color: #F7F7F7;
259 - bottom: 0; 276 + padding: 1rem 1.5rem;
260 - left: 0; 277 + }
261 - display: flex; 278 +
262 - align-items: center; 279 + .book-video-language {
263 - box-sizing: content-box;
264 - background-color: white;
265 padding: 1rem; 280 padding: 1rem;
266 - .text { 281 +
267 - display: flex; 282 + .uncheck {
268 - flex-direction: column; 283 + background: #F7F7F7;
269 - justify-content: center; 284 + border-radius: 15px;
270 - min-width: 3rem; 285 + padding: 0.5rem;
271 - color: #713610;
272 text-align: center; 286 text-align: center;
287 + color: #222222;
288 + margin: 0 0.25rem;
273 } 289 }
274 - .button { 290 +
275 - display: flex; 291 + .checked {
276 - flex-direction: column; 292 + background: #F9D95C;
277 - justify-content: center; 293 + border-radius: 15px;
278 - flex: 1; 294 + padding: 0.5rem;
279 - padding: 0 0.5rem; 295 + text-align: center;
296 + color: #222222;
297 + margin: 0 0.25rem;
280 } 298 }
299 +
300 + .choose-wrapper {
301 + background: #F7F7F7;
302 + border-radius: 15px;
303 + padding: 0.5rem;
304 + text-align: center;
305 + color: #B0B0B0;
306 + margin: 0 0.25rem;
307 +
308 + .text {
309 + display: inline-block;
310 + text-align: left;
311 + width: 80%;
312 + }
313 +
314 + .icon {
315 + display: inline-block;
316 + text-align: right;
317 + width: 20%;
318 + }
319 + }
320 + }
321 +
322 + .book-video-list {
323 + height: 20rem;
324 + overflow: scroll;
281 } 325 }
282 } 326 }
327 +
328 + .book-bar {
329 + position: fixed;
330 + right: 0;
331 + bottom: 0;
332 + left: 0;
333 + display: flex;
334 + align-items: center;
335 + box-sizing: content-box;
336 + background-color: white;
337 + padding: 1rem;
338 +
339 + .text {
340 + display: flex;
341 + flex-direction: column;
342 + justify-content: center;
343 + min-width: 3rem;
344 + color: #713610;
345 + text-align: center;
346 + }
347 +
348 + .button {
349 + display: flex;
350 + flex-direction: column;
351 + justify-content: center;
352 + flex: 1;
353 + padding: 0 0.5rem;
354 + }
355 + }
356 +}
283 </style> 357 </style>
...\ No newline at end of file ...\ No newline at end of file
......