homeLand.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. <template>
  2. <view class="home-land-container">
  3. <!-- 第三层:背景 -->
  4. <view class="background-layer"></view>
  5. <!-- 第二层:地图 -->
  6. <view class="map-layer" id="mapLayer" :style="{ transform: `translateX(${translateX}px)` }" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd" @mousedown="onmousedown" @mousemove="onmousemove" @mouseup="onmouseup">
  7. <image class="island-image" src="/static/island/island.png" mode="widthFix" style="width:1536rpx; bottom: 0rpx;left: 0rpx; position: absolute;"></image>
  8. <view style="position: absolute;width: 670rpx;left: 340rpx; bottom:320rpx;align-items: center;">
  9. <image class="house-image" src="/static/island/building/home1.png" mode="widthFix" style="width:670rpx; position: static;" @click="onHouseClick" :animation="houseAnimationData"> </image>
  10. </view>
  11. <view style="position: absolute;width: 300rpx;left:280rpx; bottom:200rpx;align-items: center;">
  12. <image class="table-image" src="/static/island/building/workTable.png" mode="widthFix" style="width:670rpx; position: static;" @click="onTableClick" :animation="tableAnimationData"> </image>
  13. </view>
  14. <view style="position: absolute;width:200rpx;left:660rpx; bottom:260rpx;align-items: center;">
  15. <image class="mailBox-image" src="/static/island/building/mailBox.png" mode="widthFix" style="width:670rpx; position: static;" > </image>
  16. </view>
  17. <view style="position: absolute;width:200rpx;left:1000rpx; bottom:450rpx;align-items: center;">
  18. <image class="taskBoard-image" src="/static/island/building/taskBoard.png" mode="widthFix" style="width:670rpx; position: static;" @click="showTask" :animation="taskAnimationData"> </image>
  19. </view>
  20. <!-- 引导箭头 -->
  21. <view class="guide-arrow" v-if="showGuideArrow" :style="{ left: guideArrowPosition.x + 'rpx', bottom: guideArrowPosition.y + 'rpx', transform: `rotate(${guideArrowPosition.r}deg)` }">
  22. <image src="/static/island/arrow.png" mode="aspectFit"></image>
  23. </view>
  24. <!-- 主岛箭头 -->
  25. <view class="main-arrow" @click="goToMainLand" :animation="mainArrowAnimation" :style="{ opacity: mainArrowVisible ? 1 : 0 }">
  26. <!-- <image src="/static/island/main_arrow.png" mode="widthFix" style="width: 100rpx;"></image> -->
  27. <view class="arrow" ></view>
  28. <text class="main-text">主岛</text>
  29. </view>
  30. </view>
  31. <!-- 第一层:UI -->
  32. <!-- <view class="ui-layer"> -->
  33. <view class="ui-content">
  34. <view class="status-bar"></view>
  35. <view class="customHead">
  36. <!-- 添加返回按钮 -->
  37. <view class="back-button" @click="goBack()">
  38. <image src="/static/island/UI/back_button.png" mode="widthFix" style="width:100rpx"></image>
  39. </view>
  40. <!-- 添加货币计数器 -->
  41. <view class="currency-display">
  42. <view class="currency-item">
  43. <image src="/static/island/UI/wd_icon_coin.png" mode="widthFix" class="currency-icon">
  44. </image>
  45. <text class="currency-value">{{ userInfo.num_gmd }}</text>
  46. </view>
  47. <view class="currency-item">
  48. <image src="/static/island/UI/wd_icon_xingyuan.png" mode="widthFix" class="currency-icon">
  49. </image>
  50. <text class="currency-value">{{ userInfo.num_gmg }}</text>
  51. </view>
  52. </view>
  53. </view>
  54. <view class="ui-buttons">
  55. <image src="/static/island/icon_backpack.png" mode="widthFix" style="width:100rpx"
  56. @click="showInventory"></image>
  57. </view>
  58. </view>
  59. <!-- </view> -->
  60. <!-- 对话框组件 -->
  61. <backpack-dialog :visible.sync="inventoryVisible" @close="onInventoryClose"></backpack-dialog>
  62. <character-dialog :visible.sync="characterVisible" @close="onCharacterClose"></character-dialog>
  63. <shop-dialog :visible.sync="shopVisible" @close="onShopClose" @buy="onShopBuy"></shop-dialog>
  64. <task-dialog class="task-dialog" :visible.sync="taskVisible" @close="onTaskClose" type="main"></task-dialog>
  65. <!-- 引导对话组件 -->
  66. <talk-guide
  67. v-if="showTalkGuide"
  68. :guide-data="currentTalkData"
  69. :player-name="playerName"
  70. :visible="showTalkGuide"
  71. @talk-complete="onTalkComplete"
  72. ></talk-guide>
  73. <!-- 制造台对话框组件 -->
  74. <crafting-dialog :visible.sync="craftingVisible" @close="craftingVisible = false"></crafting-dialog>
  75. <!-- 引导管理器 -->
  76. <guide-manager ref="guideManager"></guide-manager>
  77. </view>
  78. </template>
  79. <script>
  80. import BackpackDialog from '@/components/dialogs/BackpackDialog.vue'
  81. import CharacterDialog from '@/components/dialogs/CharacterDialog.vue'
  82. import ShopDialog from '@/components/dialogs/ShopDialog.vue'
  83. import TaskDialog from './TaskDialog.vue'
  84. import TalkGuide from './talkGuide.vue'
  85. import CraftingDialog from '@/components/dialogs/CraftingDialog.vue'
  86. import GuideManager from '@/components/guide/GuideManager.vue'
  87. export default {
  88. components: {
  89. BackpackDialog,
  90. CharacterDialog,
  91. ShopDialog,
  92. TaskDialog,
  93. TalkGuide,
  94. CraftingDialog,
  95. GuideManager
  96. },
  97. data() {
  98. return {
  99. // 背景位置控制
  100. translateX: -141,
  101. startX: 0,
  102. currentX: 0,
  103. isDragging : false,
  104. // 获取屏幕宽度和背景宽度
  105. screenWidth: 0,
  106. backgroundWidth: 0,
  107. islandHeight: 0,
  108. houseAnimationData: {},
  109. tableAnimationData: {},
  110. taskAnimationData: {},
  111. houseAnimating: false,
  112. tableAnimating: false,
  113. taskAnimating: false,
  114. baseTranslate: { x: -50, y: 50 },
  115. inventoryVisible: false,
  116. characterVisible: false,
  117. shopVisible: false,
  118. mainArrowAnimation: {},
  119. mainArrowAnimating: false,
  120. mainArrowVisible: false,
  121. taskVisible: false,
  122. playerName: '梦幻',
  123. guideData: [
  124. {
  125. characterImage: '/static/island/npc.png',
  126. characterName: '罗宾',
  127. text: '这位大人,这里太大了吧....',
  128. position: 'left',
  129. isMirror: true
  130. },
  131. {
  132. characterImage: '/static/island/building/1.png',
  133. characterName: '我',
  134. text: '好的,我马上上会教付清',
  135. position: 'right',
  136. isMirror: false
  137. },
  138. {
  139. characterImage: '/static/island/building/1.png',
  140. characterName: '我',
  141. text: '很奇怪不是吗?',
  142. position: 'right',
  143. isMirror: false
  144. },
  145. {
  146. characterImage: '/static/island/npc.png',
  147. characterName: '罗宾',
  148. text: '怎么回事',
  149. position: 'left',
  150. isMirror: true
  151. }
  152. ],
  153. craftingVisible: false,
  154. guideManager: null,
  155. userInfo: {
  156. num_gmd: 0,
  157. num_gmg: 0,
  158. },
  159. moneyTimer: null, // 添加定时器变量
  160. showTalkGuide: false,
  161. currentTalkData: [],
  162. // 引导箭头相关
  163. showGuideArrow: false,
  164. guideArrowPosition: { x: 0, y: 0, r: 0 }
  165. }
  166. },
  167. onLoad() {
  168. let self = this;
  169. // 获取屏幕宽度
  170. uni.getSystemInfo({
  171. success: (res) => {
  172. self.screenWidth = res.windowWidth;
  173. console.log('屏幕宽度:', self.screenWidth);
  174. }
  175. });
  176. this.getUserMoney();
  177. // 启动定时器,每2秒更新一次铃钱
  178. this.moneyTimer = setInterval(() => {
  179. this.getUserMoney();
  180. }, 2000);
  181. },
  182. onShow() {
  183. // 检查是否需要显示引导
  184. this.checkAndShowGuide('homeLand');
  185. },
  186. onReady() {
  187. // 在组件渲染完成后获取图片尺寸
  188. setTimeout(() => {
  189. this.getImageSize();
  190. // 延迟1秒后显示箭头并开始动画
  191. setTimeout(() => {
  192. this.mainArrowVisible = true;
  193. this.startMainArrowAnimation();
  194. }, 1000);
  195. }, 300);
  196. },
  197. onUnload() {
  198. // 清除定时器
  199. if (this.moneyTimer) {
  200. clearInterval(this.moneyTimer);
  201. this.moneyTimer = null;
  202. }
  203. },
  204. methods: {
  205. loadData() {
  206. // 可以在这里加载其他数据
  207. },
  208. getImageSize() {
  209. const query = uni.createSelectorQuery().in(this);
  210. query.select('.island-image').boundingClientRect(data => {
  211. if (data) {
  212. // 获取岛屿图片的宽度和高度
  213. this.backgroundWidth = data.width;
  214. this.islandHeight = data.height;
  215. // 计算最大可移动距离
  216. this.maxTranslate = this.backgroundWidth - this.screenWidth;
  217. // 初始位置居中
  218. // this.translateX = -this.maxTranslate / 2;
  219. // this.translateX = 0;
  220. // 打印调试信息
  221. // console.log('屏幕宽度:', this.screenWidth);
  222. // console.log('背景宽度:', this.backgroundWidth);
  223. // console.log('岛屿高度:', this.islandHeight);
  224. // console.log('最大可移动距离:',this.maxTranslate);
  225. // console.log('岛屿data:', data);
  226. } else {
  227. console.error('未能获取岛屿图片的尺寸');
  228. }
  229. }).exec();
  230. },
  231. // 触摸开始
  232. touchStart(e) {
  233. this.startX = e.touches[0].clientX;
  234. this.currentX = this.translateX;
  235. console.log('this.startX =', this.startX);
  236. console.log('this.currentX =', this.currentX);
  237. },
  238. // 触摸移动
  239. touchMove(e) {
  240. console.log('----------- touchMove');
  241. const moveX = e.touches[0].clientX - this.startX;
  242. let newTranslateX = this.currentX + moveX;
  243. // 限制移动范围,不让背景两侧露出
  244. if (newTranslateX > 0) {
  245. newTranslateX = 0;
  246. } else if (newTranslateX < -this.maxTranslate) {
  247. newTranslateX = -this.maxTranslate;
  248. }
  249. this.translateX = newTranslateX;
  250. console.log('moveX =',moveX);
  251. console.log('this.translateX =',this.translateX);
  252. },
  253. // 触摸结束
  254. touchEnd() {
  255. console.log('----------- touchEnd');
  256. this.currentX = this.translateX;
  257. console.log('this.currentX =',this.currentX);
  258. },
  259. onmousedown(e) {
  260. console.log('----------- onmousedown');
  261. console.log('----------- e',e);
  262. this.isDragging = true;
  263. this.startX = e.clientX;
  264. this.currentX = this.translateX;
  265. mapLayer.style.cursor = 'grabbing';
  266. },
  267. onmousemove(e) {
  268. if(this.isDragging){
  269. console.log('----------- onmousemove');
  270. const moveX = e.clientX - this.startX;
  271. let newTranslateX = this.currentX + moveX;
  272. //限制移动范围,不让背景两侧露出
  273. if (newTranslateX > 0) {
  274. newTranslateX = 0;
  275. } else if (newTranslateX < -this.maxTranslate) {
  276. newTranslateX = -this.maxTranslate;
  277. }
  278. this.translateX = newTranslateX;
  279. console.log('moveX =',moveX);
  280. console.log('this.translateX =',this.translateX);
  281. }
  282. },
  283. onmouseup(e) {
  284. console.log('----------- onmouseup');
  285. this.isDragging = false;
  286. mapLayer.style.cursor = 'grab';
  287. },
  288. onHouseClick(event) {
  289. if(this.houseAnimating) return;
  290. // 处理点击事件
  291. console.log('House clicked!');
  292. // 播放房子的点击动画
  293. this.playAnimation('house');
  294. // uni.showToast({
  295. // title: 'House clicked!',
  296. // icon: 'none'
  297. // });
  298. },
  299. onTableClick(event) {
  300. if(this.tableAnimating) return;
  301. console.log('Table clicked!');
  302. // 播放大厅的点击动画
  303. this.playAnimation('table');
  304. // 显示制造台界面
  305. this.craftingVisible = true;
  306. this.checkAndShowGuide('homeLand_table');
  307. },
  308. onTaskClick(event) {
  309. if(this.taskAnimating) return;
  310. // 处理点击事件
  311. console.log('Task clicked!');
  312. // 播放大厅的点击动画
  313. this.playAnimation('task');
  314. // uni.showToast({
  315. // title: 'task clicked!',
  316. // icon: 'none'
  317. // });
  318. },
  319. playAnimation(type) {
  320. let self = this;
  321. // 根据类型设置对应的动画状态
  322. if (type === 'house') {
  323. this.houseAnimating = true;
  324. } else if (type === 'table') {
  325. this.tableAnimating = true;
  326. } else if (type === 'task') {
  327. this.taskAnimating = true;
  328. }
  329. // 创建动画实例
  330. const animation = uni.createAnimation({
  331. duration: 400,
  332. timingFunction: 'ease',
  333. });
  334. // 定义果冻动画序列
  335. animation.scale(0.95).step({ duration: 100 })
  336. .scale(1.05).step({ duration: 100 })
  337. .scale(0.98).step({ duration: 100 })
  338. .scale(1).step({ duration: 100 });
  339. // 根据类型应用动画到对应的元素
  340. if (type === 'house') {
  341. this.houseAnimationData = animation.export();
  342. } else if (type === 'table') {
  343. this.tableAnimationData = animation.export();
  344. } else if (type === 'task') {
  345. this.taskAnimationData = animation.export();
  346. }
  347. // 动画结束后重置状态
  348. setTimeout(() => {
  349. if (type === 'house') {
  350. self.houseAnimating = false;
  351. } else if (type === 'table') {
  352. self.tableAnimating = false;
  353. } else if (type === 'task') {
  354. self.taskAnimating = false;
  355. }
  356. }, 800);
  357. },
  358. showInventory() {
  359. console.log('Opening inventory...')
  360. this.inventoryVisible = true
  361. },
  362. onInventoryClose() {
  363. console.log('Closing inventory...')
  364. this.inventoryVisible = false
  365. },
  366. showCharacter() {
  367. console.log('Opening character...')
  368. this.characterVisible = true
  369. },
  370. onCharacterClose() {
  371. console.log('Closing character...')
  372. this.characterVisible = false
  373. },
  374. showShop() {
  375. console.log('Opening shop...')
  376. this.shopVisible = true
  377. },
  378. onShopClose() {
  379. console.log('Closing shop...')
  380. this.shopVisible = false
  381. },
  382. onShopBuy(item) {
  383. console.log('Buying item:', item)
  384. },
  385. showTask() {
  386. console.log('Opening task board...')
  387. this.taskVisible = true;
  388. this.checkAndShowGuide('homeLand_TaskBoard');
  389. },
  390. onTaskClose() {
  391. console.log('Closing task board...')
  392. this.taskVisible = false
  393. },
  394. startMainArrowAnimation() {
  395. if (this.mainArrowAnimating) return;
  396. this.mainArrowAnimating = true;
  397. const animation = uni.createAnimation({
  398. duration: 1000,
  399. timingFunction: 'ease-in-out',
  400. });
  401. const animate = () => {
  402. if (!this.mainArrowAnimating) return;
  403. animation.translateY(-10).step({ duration: 1000 });
  404. this.mainArrowAnimation = animation.export();
  405. setTimeout(() => {
  406. animation.translateY(0).step({ duration: 1000 });
  407. this.mainArrowAnimation = animation.export();
  408. setTimeout(() => {
  409. if (this.mainArrowAnimating) {
  410. animate();
  411. }
  412. }, 1000);
  413. }, 1000);
  414. };
  415. animate();
  416. },
  417. goToMainLand() {
  418. uni.navigateTo({
  419. url: '/pages/isLand/mainLand'
  420. });
  421. },
  422. // 检查并显示引导
  423. checkAndShowGuide(stageId) {
  424. console.log('checkAndShowGuide stageId :', stageId);
  425. // 延迟执行以确保DOM已经渲染
  426. setTimeout(() => {
  427. if (this.$refs.guideManager) {
  428. const hasGuide = this.$refs.guideManager.startGuide(stageId);
  429. console.log('checkAndShowGuide hasGuide :', hasGuide);
  430. if (hasGuide) {
  431. this.currentTalkData = this.$refs.guideManager.getCurrentTalkData();
  432. console.log('checkAndShowGuide currentTalkData :', this.currentTalkData);
  433. if (this.currentTalkData) {
  434. this.showTalkGuide = true;
  435. // 检查是否需要显示引导箭头
  436. this.checkAndShowGuideArrow();
  437. }else{
  438. console.log('currentTalkData err:', this.currentTalkData);
  439. }
  440. }
  441. }
  442. }, 200);
  443. },
  444. // 获取用户铃钱
  445. getUserMoney() {
  446. uni.request({
  447. url: this.$apiHost + '/User/getinfo',
  448. method: 'GET',
  449. data: {
  450. uuid: getApp().globalData.uuid
  451. },
  452. header: {
  453. 'Content-Type': 'application/x-www-form-urlencoded',
  454. 'sign': getApp().globalData.headerSign,
  455. },
  456. success: (res) => {
  457. if (res.data) {
  458. console.log("res.getUserMoney", res.data)
  459. this.userInfo = res.data;
  460. }
  461. }
  462. })
  463. },
  464. // 对话完成回调
  465. onTalkComplete() {
  466. // 调用引导管理器的下一步
  467. console.log('----------- onTalkComplete');
  468. this.$refs.guideManager && this.$refs.guideManager.nextStep();
  469. // 获取新的对话数据
  470. const newTalkData = this.$refs.guideManager.getCurrentTalkData();
  471. console.log('----------- onTalkComplete newTalkData:', newTalkData);
  472. if (newTalkData) {
  473. // 先隐藏对话组件
  474. this.showTalkGuide = false;
  475. // 更新对话数据
  476. this.currentTalkData = newTalkData;
  477. // 使用 nextTick 确保数据更新后再显示对话组件
  478. this.$nextTick(() => {
  479. this.showTalkGuide = true;
  480. // 检查是否需要显示引导箭头
  481. this.checkAndShowGuideArrow();
  482. });
  483. } else {
  484. // 如果没有新的对话数据,隐藏对话组件
  485. this.showTalkGuide = false;
  486. // 隐藏引导箭头
  487. // this.showGuideArrow = false;
  488. }
  489. },
  490. // 检查并显示引导箭头
  491. checkAndShowGuideArrow() {
  492. if (this.$refs.guideManager) {
  493. const arrowPosition = this.$refs.guideManager.getCurrentArrowPosition();
  494. console.log('----------- checkAndShowGuideArrow arrowPosition:', arrowPosition);
  495. if (arrowPosition) {
  496. // 显示引导箭头
  497. this.showGuideArrow = true;
  498. // 设置箭头位置
  499. this.guideArrowPosition = {
  500. x: arrowPosition.x,
  501. y: arrowPosition.y,
  502. r: arrowPosition.r || 0
  503. };
  504. console.log('----------- guideArrowPosition:', this.guideArrowPosition);
  505. console.log('----------- showGuideArrow:', this.showGuideArrow);
  506. } else {
  507. // 隐藏引导箭头
  508. this.showGuideArrow = false;
  509. }
  510. }
  511. },
  512. goBack() {
  513. uni.navigateBack({
  514. delta: 1
  515. });
  516. },
  517. },
  518. beforeDestroy() {
  519. this.mainArrowAnimating = false;
  520. }
  521. }
  522. </script>
  523. <style lang="scss" scoped>
  524. @import './homeLand.scss';
  525. // .ui-layer {
  526. // position: fixed;
  527. // top: 0;
  528. // left: 0;
  529. // right: 0;
  530. // bottom: 0;
  531. // pointer-events: none;
  532. // z-index: 100;
  533. .ui-content {
  534. position: fixed;
  535. top: 0;
  536. left: 0;
  537. width: 100%;
  538. pointer-events: auto;
  539. z-index: 101;
  540. }
  541. .ui-buttons {
  542. display: flex;
  543. flex-direction: column;
  544. gap: 20rpx;
  545. .ui-button {
  546. background: rgba(255, 255, 255, 0.9);
  547. border: none;
  548. padding: 16rpx 32rpx;
  549. border-radius: 8rpx;
  550. font-size: 28rpx;
  551. color: #333;
  552. box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
  553. min-width: 120rpx;
  554. &:active {
  555. opacity: 0.8;
  556. transform: scale(0.95);
  557. }
  558. }
  559. }
  560. // }
  561. .map-layer {
  562. position: relative;
  563. z-index: 1;
  564. cursor: grab;
  565. &:active {
  566. cursor: grabbing;
  567. }
  568. }
  569. .guide-arrow {
  570. position: absolute;
  571. z-index: 10;
  572. width: 100rpx;
  573. height: 100rpx;
  574. display: flex;
  575. justify-content: center;
  576. align-items: center;
  577. pointer-events: none; /* 防止箭头阻挡点击事件 */
  578. image {
  579. width: 100rpx;
  580. height: 100rpx;
  581. animation: pulse 1.5s infinite; /* 添加脉冲动画 */
  582. }
  583. }
  584. @keyframes pulse {
  585. 0% {
  586. transform: scale(1);
  587. opacity: 1;
  588. }
  589. 50% {
  590. transform: scale(1.2);
  591. opacity: 0.8;
  592. }
  593. 100% {
  594. transform: scale(1);
  595. opacity: 1;
  596. }
  597. }
  598. .main-arrow {
  599. // background: rgba(255, 255, 255, 0.9);
  600. position: absolute;
  601. height: 190rpx;
  602. right: 50rpx;
  603. bottom: 20rpx; // 改为 bottom 定位
  604. display: flex;
  605. flex-direction: column;
  606. align-items: center;
  607. cursor: pointer;
  608. z-index: 10;
  609. transition: opacity 0.5s ease-in-out;
  610. .arrow {
  611. content: '';
  612. display: block;
  613. width: 60rpx;
  614. height: 60rpx;
  615. background: url('/static/island/arrow.png') no-repeat center center;
  616. background-size: contain;
  617. transform: rotate(270deg);
  618. }
  619. .main-text {
  620. color: #ffffff;
  621. font-size: 32rpx;
  622. text-shadow: 2rpx 2rpx 4rpx rgba(0, 0, 0, 0.5);
  623. margin-top: 10rpx;
  624. }
  625. }
  626. .task-dialog{
  627. ::v-deep.dialog-content{
  628. background: transparent !important;
  629. }
  630. }
  631. </style>