init
2
.browserslistrc
Normal file
@ -0,0 +1,2 @@
|
||||
> 1%
|
||||
last 2 versions
|
21
.gitignore
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2019 diaogroup
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
20
README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# 曳光控制台
|
||||
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
5
babel.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
12007
package-lock.json
generated
Normal file
38
package.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "ygadmin",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"buildsuper": "vue-cli-service build --modern"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.2",
|
||||
"core-js": "^3.6.4",
|
||||
"crc-32": "^1.2.0",
|
||||
"crypto-js": "^4.0.0",
|
||||
"lrz": "^4.9.40",
|
||||
"material-design-icons": "^3.0.1",
|
||||
"register-service-worker": "^1.6.2",
|
||||
"roboto-fontface": "^0.6.0",
|
||||
"vconsole": "^3.3.4",
|
||||
"vue": "^2.6.11",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-material": "^1.0.0-beta-11",
|
||||
"vue-router": "^3.1.5",
|
||||
"vue-svg-icon": "^1.2.9",
|
||||
"vue-touch": "^2.0.0-beta.4",
|
||||
"vuex": "^3.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.2.0",
|
||||
"@vue/cli-plugin-pwa": "^4.2.0",
|
||||
"@vue/cli-plugin-router": "^4.2.0",
|
||||
"@vue/cli-plugin-vuex": "^4.2.0",
|
||||
"@vue/cli-service": "^4.2.0",
|
||||
"node-sass": "^4.12.0",
|
||||
"sass-loader": "^8.0.2",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
}
|
||||
}
|
BIN
public/favicon.ico
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
public/img/icons/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
public/img/icons/android-chrome-512x512.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
public/img/icons/apple-touch-icon-120x120.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
public/img/icons/apple-touch-icon-152x152.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
public/img/icons/apple-touch-icon-180x180.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
public/img/icons/apple-touch-icon-60x60.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
public/img/icons/apple-touch-icon-76x76.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
public/img/icons/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
public/img/icons/favicon-16x16.png
Normal file
After Width: | Height: | Size: 387 B |
BIN
public/img/icons/favicon-32x32.png
Normal file
After Width: | Height: | Size: 726 B |
BIN
public/img/icons/msapplication-icon-144x144.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
public/img/icons/mstile-150x150.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
1
public/img/icons/safari-pinned-tab.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1582191167349" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3270" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M921.6 716.75904L415.46752 210.66752a136.25344 136.25344 0 0 0-96.54272-39.99744c-70.93248 0-128.53248 54.272-135.19872 123.43296L102.4 375.43936h86.12864C210.59584 506.39872 294.57408 616.09984 409.6 673.49504v179.84512h68.27008V700.34432a405.18656 405.18656 0 0 0 68.25984 13.70112v139.30496H614.4V716.75904h307.2zM448.93184 340.6336L620.2368 511.95904h-39.95648c-75.27424 0-136.54016-61.22496-136.54016-136.52992-0.01024-12.12416 2.12992-23.63392 5.19168-34.79552z m-198.26688-33.46432a68.32128 68.32128 0 0 1 68.25984-68.22912c18.19648 0 35.36896 7.09632 48.27136 19.99872l28.4672 28.4672a202.37312 202.37312 0 0 0-20.20352 88.03328c0 112.92672 91.86304 204.8 204.8 204.8h108.22656l68.27008 68.25984H592.00512c-188.20096 0-341.34016-153.1392-341.34016-341.32992z" p-id="3271" fill="#bbdefb"></path></svg>
|
After Width: | Height: | Size: 1.2 KiB |
38
public/index.html
Normal file
@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title>曳光</title>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<link rel="apple-touch-icon-precomposed" href="<%= BASE_URL %>favicon.ico">
|
||||
<!-- 标题 -->
|
||||
<meta itemprop="name" content="曳光控制台" />
|
||||
<!-- 描述 -->
|
||||
<meta itemprop="description" content="负责进行曳光内容审核" />
|
||||
<!-- 图片 -->
|
||||
<meta itemprop="image" content="http://canary.moe/img/canary.png" />
|
||||
<script>
|
||||
try {
|
||||
console.log('%cRainSun Copyright \xa9 2019-%s',
|
||||
'font-size:12px;color:#999999;', (new Date).getFullYear());
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
|
||||
Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
|
||||
</html>
|
2
public/robots.txt
Normal file
@ -0,0 +1,2 @@
|
||||
User-agent: *
|
||||
Disallow:
|
205
src/App.vue
Normal file
@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<icon class="bg" name="canary"></icon>
|
||||
<!-- 缓存 -->
|
||||
<keep-alive>
|
||||
<router-view v-if="this.$route.meta.keepAlive"></router-view>
|
||||
</keep-alive>
|
||||
<!-- 非缓存 -->
|
||||
<router-view v-if="!this.$route.meta.keepAlive"></router-view>
|
||||
<md-bottom-bar
|
||||
style="position: fixed; bottom: 0; left: 0; width: 100%;"
|
||||
md-sync-route
|
||||
v-if="bar_state"
|
||||
>
|
||||
<md-bottom-bar-item
|
||||
id="bottom-bar-item-home"
|
||||
md-icon="home"
|
||||
md-label="主页"
|
||||
to="/home"
|
||||
></md-bottom-bar-item>
|
||||
<md-bottom-bar-item
|
||||
id="bottom-bar-item-create"
|
||||
md-icon="near_me"
|
||||
md-label="创建"
|
||||
to="/create"
|
||||
></md-bottom-bar-item>
|
||||
<md-bottom-bar-item
|
||||
id="bottom-bar-item-mine"
|
||||
md-icon="person"
|
||||
md-label="我的"
|
||||
to="/account"
|
||||
></md-bottom-bar-item>
|
||||
</md-bottom-bar>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
export default {
|
||||
name: 'App',
|
||||
computed: {
|
||||
...mapState(["bar_state"])
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@import "./style/main";
|
||||
@import "./style/font";
|
||||
@import "~vue-material/dist/theme/engine";
|
||||
@import "~vue-material/dist/theme/all";
|
||||
body,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
hr,
|
||||
p,
|
||||
blockquote,
|
||||
dl,
|
||||
dt,
|
||||
dd,
|
||||
ul,
|
||||
ol,
|
||||
li,
|
||||
pre,
|
||||
form,
|
||||
fieldset,
|
||||
legend,
|
||||
button,
|
||||
input,
|
||||
textarea,
|
||||
th,
|
||||
td {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body,
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font: 12px/1.5tahoma, arial, \5b8b\4f53;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: 100%;
|
||||
}
|
||||
address,
|
||||
cite,
|
||||
dfn,
|
||||
em,
|
||||
var {
|
||||
font-style: normal;
|
||||
}
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family: couriernew, courier, monospace;
|
||||
}
|
||||
small {
|
||||
font-size: 12px;
|
||||
}
|
||||
ul,
|
||||
ol {
|
||||
list-style: none;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
sup {
|
||||
vertical-align: text-top;
|
||||
}
|
||||
sub {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
legend {
|
||||
color: #000;
|
||||
}
|
||||
fieldset,
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-size: 100%;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
button {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
html[data-theme="dark"] {
|
||||
@include set-theme-dark();
|
||||
@import "~vue-material/dist/theme/all";
|
||||
.md-caption {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
}
|
||||
html[data-theme="light"] {
|
||||
@include set-theme-light();
|
||||
@import "~vue-material/dist/theme/all";
|
||||
}
|
||||
#app {
|
||||
font-family: "Roboto", "Helvetica Neue", Helvetica, "PingFang SC",
|
||||
"Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
/* text-align: center; */
|
||||
// color: #2c3e50;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.bg {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
z-index: -100;
|
||||
height: 5rem;
|
||||
width: 5rem;
|
||||
margin-top: -2.5rem;
|
||||
margin-left: -2.5rem;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
.md-autocomplete-box-content {
|
||||
left: 0.2rem !important;
|
||||
}
|
||||
}
|
||||
#__vconsole {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vconsole-show {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.md-bottom-bar.md-type-fixed .md-bottom-bar-item {
|
||||
max-width: 1000px !important;
|
||||
}
|
||||
</style>
|
BIN
src/assets/KFOmCnqEu92Fr1Mu4WxKOzY.woff2
Normal file
BIN
src/assets/MaterialIcons-Regular.eot
Normal file
BIN
src/assets/MaterialIcons-Regular.ttf
Normal file
BIN
src/assets/MaterialIcons-Regular.woff
Normal file
BIN
src/assets/MaterialIcons-Regular.woff2
Normal file
BIN
src/assets/canary.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/assets/logo.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
23
src/axios/fetch.js
Normal file
@ -0,0 +1,23 @@
|
||||
import axios from 'axios'
|
||||
|
||||
export const api = axios.create({
|
||||
baseURL: 'https://yg.canary.moe/v1/user/',
|
||||
// baseURL: window.location.origin + '/api/',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*'
|
||||
},
|
||||
timeout: 30 * 1000
|
||||
})
|
||||
|
||||
//设置拦截器
|
||||
api.interceptors.response.use(
|
||||
(response) => {
|
||||
// console.log('拦截器:请求成功', response)
|
||||
return response
|
||||
}, (error) => {
|
||||
// console.log('拦截器:发生错误', error.response)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
40
src/main.js
Normal file
@ -0,0 +1,40 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import './registerServiceWorker'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import { setHtmlFontSize } from './utils/px2rem.js'
|
||||
// icon-loader
|
||||
import Icon from 'vue-svg-icon/Icon.vue'
|
||||
Vue.component('icon', Icon);
|
||||
|
||||
// material
|
||||
import VueMaterial from 'vue-material'
|
||||
import 'vue-material/dist/vue-material.min.css'
|
||||
import 'vue-material/dist/theme/default.css'
|
||||
// import 'vue-material/dist/theme/default-dark.css'
|
||||
import 'material-design-icons/iconfont/material-icons.css'
|
||||
Vue.use(VueMaterial)
|
||||
|
||||
//px2rem
|
||||
setHtmlFontSize();
|
||||
|
||||
// 剪切板
|
||||
import VueClipboard from "vue-clipboard2";
|
||||
Vue.use(VueClipboard);
|
||||
|
||||
// 手势控件
|
||||
var VueTouch = require('vue-touch')
|
||||
Vue.use(VueTouch, {name: 'v-touch'})
|
||||
|
||||
// vconsole
|
||||
import Vconsole from 'vconsole';
|
||||
new Vconsole();
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
33
src/registerServiceWorker.js
Normal file
@ -0,0 +1,33 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import { register } from 'register-service-worker'
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
register(`${process.env.BASE_URL}service-worker.js`, {
|
||||
ready () {
|
||||
console.log(
|
||||
'App is being served from cache by a service worker.\n' +
|
||||
'For more details, visit https://goo.gl/AFskqB'
|
||||
)
|
||||
},
|
||||
registered () {
|
||||
console.log('Service worker has been registered.')
|
||||
},
|
||||
cached () {
|
||||
console.log('Content has been cached for offline use.')
|
||||
},
|
||||
updatefound () {
|
||||
console.log('New content is downloading.')
|
||||
},
|
||||
updated () {
|
||||
console.log('New content is available; please refresh.')
|
||||
window.location.reload(true)
|
||||
},
|
||||
offline () {
|
||||
console.log('No internet connection found. App is running in offline mode.')
|
||||
},
|
||||
error (error) {
|
||||
console.error('Error during service worker registration:', error)
|
||||
}
|
||||
})
|
||||
}
|
53
src/router/index.js
Normal file
@ -0,0 +1,53 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/home',
|
||||
name: 'Home',
|
||||
component: () => import(/* webpackChunkName: "verify" */ '../views/Home/Home.vue'),
|
||||
meta: {
|
||||
keepAlive: true
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/create',
|
||||
name: 'Create',
|
||||
component: () => import(/* webpackChunkName: "create" */ '../views/Create/Create.vue'),
|
||||
meta: {
|
||||
keepAlive: false
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/account',
|
||||
name: 'Account',
|
||||
component: () => import(/* webpackChunkName: "account" */ '../views/Account/Account.vue'),
|
||||
meta: {
|
||||
keepAlive: true
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/detail',
|
||||
name: 'Detail',
|
||||
component: () => import(/* webpackChunkName: "detail" */ '../views/Detail/Detail.vue'),
|
||||
meta: {
|
||||
keepAlive: false
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '*', // 页面不存在的情况下会跳到home
|
||||
redirect: '/home',
|
||||
name: 'notFound',
|
||||
hidden: true
|
||||
}
|
||||
]
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
base: process.env.BASE_URL,
|
||||
routes,
|
||||
})
|
||||
|
||||
export default router
|
69
src/store/index.js
Normal file
@ -0,0 +1,69 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
// 用户信息
|
||||
user_info: {},
|
||||
// 配置信息
|
||||
settings: {
|
||||
open_vconsole: false,
|
||||
is_dark_mode: false,
|
||||
},
|
||||
// 失物招领信息
|
||||
laf: [],
|
||||
// bottomBar显示状态
|
||||
bar_state: true
|
||||
},
|
||||
mutations: {
|
||||
// 设置用户信息
|
||||
SET_USERINFO(state, user_info) {
|
||||
state.user_info = user_info;
|
||||
},
|
||||
// 设置配置信息
|
||||
SET_SETTINGS(state, settings) {
|
||||
state.settings = settings;
|
||||
},
|
||||
// 设置失物招领信息
|
||||
SET_LAF(state, laf) {
|
||||
state.laf = laf;
|
||||
},
|
||||
// 设置bottomBar显示状态
|
||||
SET_BARSTATE(state, bar_state) {
|
||||
state.bar_state = bar_state;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// 设置用户信息
|
||||
setUserInfo({
|
||||
commit
|
||||
}, arg) {
|
||||
commit('SET_USERINFO', arg[0]);
|
||||
localStorage.setItem("storeState", JSON.stringify(arg[1].$store.state));
|
||||
},
|
||||
// 设置配置信息
|
||||
setSettings({
|
||||
commit
|
||||
}, arg) {
|
||||
commit('SET_SETTINGS', arg[0]);
|
||||
localStorage.setItem("storeState", JSON.stringify(arg[1].$store.state));
|
||||
},
|
||||
// 设置失物招领信息
|
||||
setLaf({
|
||||
commit
|
||||
}, arg) {
|
||||
commit('SET_LAF', arg[0]);
|
||||
localStorage.setItem("storeState", JSON.stringify(arg[1].$store.state));
|
||||
},
|
||||
// 设置bottomBar显示状态
|
||||
setBarState({
|
||||
commit
|
||||
}, arg) {
|
||||
commit('SET_BARSTATE', arg[0]);
|
||||
localStorage.setItem("storeState", JSON.stringify(arg[1].$store.state));
|
||||
},
|
||||
},
|
||||
modules: {}
|
||||
})
|
99
src/style/font.scss
Normal file
@ -0,0 +1,99 @@
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu72xKOzY.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu5mxKOzY.woff2) format('woff2');
|
||||
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7mxKOzY.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu4WxKOzY.woff2) format('woff2');
|
||||
unicode-range: U+0370-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7WxKOzY.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7GxKOzY.woff2) format('woff2');
|
||||
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu4mxK.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
// @font-face {
|
||||
// font-family: 'Material Icons';
|
||||
// font-style: normal;
|
||||
// font-weight: 400;
|
||||
// src: url(/assets/MaterialIcons-Regular.eot); /* For IE6-8 */
|
||||
// src: local('Material Icons'),
|
||||
// local('MaterialIcons-Regular'),
|
||||
// url(/assets/MaterialIcons-Regular.woff2) format('woff2'),
|
||||
// url(/assets/MaterialIcons-Regular.woff) format('woff'),
|
||||
// url(/assets/MaterialIcons-Regular.ttf) format('truetype');
|
||||
// }
|
||||
|
||||
// .material-icons {
|
||||
// font-family: 'Material Icons';
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// font-size: 24px; /* Preferred icon size */
|
||||
// display: inline-block;
|
||||
// line-height: 1;
|
||||
// text-transform: none;
|
||||
// letter-spacing: normal;
|
||||
// word-wrap: normal;
|
||||
// white-space: nowrap;
|
||||
// direction: ltr;
|
||||
|
||||
// /* Support for all WebKit browsers. */
|
||||
// -webkit-font-smoothing: antialiased;
|
||||
// /* Support for Safari and Chrome. */
|
||||
// text-rendering: optimizeLegibility;
|
||||
|
||||
// /* Support for Firefox. */
|
||||
// -moz-osx-font-smoothing: grayscale;
|
||||
|
||||
// /* Support for IE. */
|
||||
// font-feature-settings: 'liga';
|
||||
// }
|
36
src/style/main.scss
Normal file
@ -0,0 +1,36 @@
|
||||
$main-color: #448aff;
|
||||
|
||||
@import '~vue-material/dist/theme/engine'; // Import the theme engine
|
||||
|
||||
@mixin set-theme-light() {
|
||||
@include md-register-theme(
|
||||
'default',
|
||||
(
|
||||
primary: md-get-palette-color(blue, A200),
|
||||
// The primary color of your application
|
||||
|
||||
accent: md-get-palette-color(red, A200),
|
||||
// The accent or secondary color
|
||||
|
||||
theme: light// This can be dark or light
|
||||
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@mixin set-theme-dark() {
|
||||
@include md-register-theme(
|
||||
'default',
|
||||
(
|
||||
primary: md-get-palette-color(blue, A200),
|
||||
// The primary color of your application
|
||||
|
||||
accent: md-get-palette-color(red, A200),
|
||||
// The accent or secondary color
|
||||
|
||||
theme: dark// This can be dark or light
|
||||
|
||||
)
|
||||
);
|
||||
}
|
||||
@import '~vue-material/dist/theme/all'; // Apply the theme
|
1
src/svg/canary-white.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="200px" height="200.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#ffffff" d="M921.6 716.75904L415.46752 210.66752a136.25344 136.25344 0 0 0-96.54272-39.99744c-70.93248 0-128.53248 54.272-135.19872 123.43296L102.4 375.43936h86.12864C210.59584 506.39872 294.57408 616.09984 409.6 673.49504v179.84512h68.27008V700.34432a405.18656 405.18656 0 0 0 68.25984 13.70112v139.30496H614.4V716.75904h307.2zM448.93184 340.6336L620.2368 511.95904h-39.95648c-75.27424 0-136.54016-61.22496-136.54016-136.52992-0.01024-12.12416 2.12992-23.63392 5.19168-34.79552z m-198.26688-33.46432a68.32128 68.32128 0 0 1 68.25984-68.22912c18.19648 0 35.36896 7.09632 48.27136 19.99872l28.4672 28.4672a202.37312 202.37312 0 0 0-20.20352 88.03328c0 112.92672 91.86304 204.8 204.8 204.8h108.22656l68.27008 68.25984H592.00512c-188.20096 0-341.34016-153.1392-341.34016-341.32992z" /></svg>
|
After Width: | Height: | Size: 1.0 KiB |
1
src/svg/canary.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1582191167349" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3270" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M921.6 716.75904L415.46752 210.66752a136.25344 136.25344 0 0 0-96.54272-39.99744c-70.93248 0-128.53248 54.272-135.19872 123.43296L102.4 375.43936h86.12864C210.59584 506.39872 294.57408 616.09984 409.6 673.49504v179.84512h68.27008V700.34432a405.18656 405.18656 0 0 0 68.25984 13.70112v139.30496H614.4V716.75904h307.2zM448.93184 340.6336L620.2368 511.95904h-39.95648c-75.27424 0-136.54016-61.22496-136.54016-136.52992-0.01024-12.12416 2.12992-23.63392 5.19168-34.79552z m-198.26688-33.46432a68.32128 68.32128 0 0 1 68.25984-68.22912c18.19648 0 35.36896 7.09632 48.27136 19.99872l28.4672 28.4672a202.37312 202.37312 0 0 0-20.20352 88.03328c0 112.92672 91.86304 204.8 204.8 204.8h108.22656l68.27008 68.25984H592.00512c-188.20096 0-341.34016-153.1392-341.34016-341.32992z" p-id="3271" fill="#bbdefb"></path></svg>
|
After Width: | Height: | Size: 1.2 KiB |
1
src/svg/file_copy.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm-1 4l6 6v10c0 1.1-.9 2-2 2H7.99C6.89 23 6 22.1 6 21l.01-14c0-1.1.89-2 1.99-2h7zm-1 7h5.5L14 6.5V12z"/></svg>
|
After Width: | Height: | Size: 274 B |
19
src/utils/aes.js
Normal file
@ -0,0 +1,19 @@
|
||||
import CryptoJS from 'crypto-js'
|
||||
|
||||
// aes加密用户密码
|
||||
export function encryptMainCode(code) {
|
||||
let default_key = 'e08b44a351a3'
|
||||
return CryptoJS.AES.encrypt(code, CryptoJS.enc.Utf8.parse(default_key), {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
}).toString();
|
||||
}
|
||||
|
||||
// aes解密用户密码
|
||||
export function decryptMainCode(row_code) {
|
||||
let default_key = 'e08b44a351a3'
|
||||
return CryptoJS.AES.decrypt(row_code, CryptoJS.enc.Utf8.parse(default_key), {
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7
|
||||
}).toString(CryptoJS.enc.Utf8);
|
||||
}
|
15
src/utils/formatTime.js
Normal file
@ -0,0 +1,15 @@
|
||||
export function formatDateTime(inputTime) {
|
||||
var date = new Date(inputTime);
|
||||
var y = date.getFullYear();
|
||||
var m = date.getMonth() + 1;
|
||||
m = m < 10 ? "0" + m : m;
|
||||
var d = date.getDate();
|
||||
d = d < 10 ? "0" + d : d;
|
||||
var h = date.getHours();
|
||||
h = h < 10 ? "0" + h : h;
|
||||
var minute = date.getMinutes();
|
||||
var second = date.getSeconds();
|
||||
minute = minute < 10 ? "0" + minute : minute;
|
||||
second = second < 10 ? "0" + second : second;
|
||||
return y + "-" + m + "-" + d + " " + h + ":" + minute + ":" + second;
|
||||
}
|
7
src/utils/px2rem.js
Normal file
@ -0,0 +1,7 @@
|
||||
//px2rem
|
||||
export function setHtmlFontSize() {
|
||||
const htmlWidth = document.documentElement.clientWidth || document.body.clientWidth
|
||||
const htmlDom = document.getElementsByTagName('html')[0]
|
||||
if (htmlWidth >= 500) htmlDom.style.fontSize = 500 / 10 + 'px'
|
||||
else htmlDom.style.fontSize = htmlWidth / 10 + 'px'
|
||||
}
|
477
src/views/Account/Account.vue
Normal file
@ -0,0 +1,477 @@
|
||||
<template>
|
||||
<div class="account" ref="account">
|
||||
<md-app md-waterfall md-mode="fixed">
|
||||
<md-app-toolbar class="md-primary">
|
||||
<div class="md-toolbar-section-start">
|
||||
<span class="md-title page-title">曳光</span>
|
||||
</div>
|
||||
<div class="md-toolbar-section-end">
|
||||
<div class="sroll-top-area"></div>
|
||||
<md-button class="md-icon-button" @click="refresh" v-if="Object.keys(this.user_info).length != 0">
|
||||
<md-icon>refresh</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
</md-app-toolbar>
|
||||
<md-app-content>
|
||||
<div ref="list_placeholder" :style="`height:40px`"></div>
|
||||
<md-progress-bar
|
||||
v-if="loading_switch"
|
||||
md-mode="indeterminate"
|
||||
style="position: absolute; top: 56px; left: 0; width: 100%;"
|
||||
></md-progress-bar>
|
||||
<template v-if="Object.keys(this.user_info).length == 0">
|
||||
<md-field style="margin-top: 30px;">
|
||||
<label>教务账号</label>
|
||||
<md-input
|
||||
v-model="cid"
|
||||
placeholder="请输入您的教务账号"
|
||||
type="number"
|
||||
maxlength="10"
|
||||
@focus="focus_list_1 = 1;focusOrBlur()"
|
||||
@blur="focus_list_1 = 0;focusOrBlur()"
|
||||
></md-input>
|
||||
</md-field>
|
||||
<md-field style="margin-top: 30px;">
|
||||
<label>教务密码</label>
|
||||
<md-input
|
||||
v-model="pwd"
|
||||
placeholder="请输入您的教务密码"
|
||||
@focus="focus_list_2 = 1;focusOrBlur()"
|
||||
@blur="focus_list_2 = 0;focusOrBlur()"
|
||||
></md-input>
|
||||
</md-field>
|
||||
<md-button class="md-raised md-primary expand login-submit" @click="login()" :disabled="loading_switch">登录</md-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<md-list>
|
||||
<md-subheader class="md-primary">用户信息</md-subheader>
|
||||
<md-list-item>
|
||||
<span class="md-list-item-text">学号:{{user_info.id}}</span>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<span class="md-list-item-text">姓名:{{user_info.name}}</span>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<span class="md-list-item-text">一卡通号:{{user_info.cid}}</span>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<span class="md-list-item-text">昵称:{{user_info.nick}}</span>
|
||||
<md-button class="md-icon-button md-list-action" @click="nick_dialog_switch = true">
|
||||
<md-icon>edit</md-icon>
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<span class="md-list-item-text">消息:{{user_info.msg.length}}条</span>
|
||||
<md-button class="md-icon-button md-list-action">
|
||||
<span>查看</span>
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<span class="md-list-item-text">已发布:{{user_info.created.length}}条</span>
|
||||
<md-button class="md-icon-button md-list-action">
|
||||
<span>查看</span>
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
<md-subheader class="md-primary">应用设置</md-subheader>
|
||||
<md-list-item>
|
||||
<md-icon>brightness_6</md-icon>
|
||||
<span class="md-list-item-text">黑暗模式</span>
|
||||
<md-switch v-model="is_dark_mode" class="md-primary"></md-switch>
|
||||
</md-list-item>
|
||||
<md-list-item>
|
||||
<md-icon>code</md-icon>
|
||||
<span class="md-list-item-text">VConsole</span>
|
||||
<md-switch v-model="open_vconsole" class="md-primary"></md-switch>
|
||||
</md-list-item>
|
||||
<md-button
|
||||
class=" md-raised md-primary expand"
|
||||
@click="logout_confirm_switch = true"
|
||||
>退出登录</md-button>
|
||||
</md-list>
|
||||
</template>
|
||||
<md-dialog-confirm
|
||||
:md-active.sync="logout_confirm_switch"
|
||||
md-title="警告"
|
||||
md-content="您将退出登录,该操作将清空本地信息"
|
||||
md-confirm-text="继续"
|
||||
md-cancel-text="取消"
|
||||
@md-confirm="logout"
|
||||
:md-click-outside-to-close="false"
|
||||
/>
|
||||
<md-dialog-prompt
|
||||
:md-active.sync="nick_dialog_switch"
|
||||
v-model="nick"
|
||||
md-title="修改昵称"
|
||||
md-input-maxlength="10"
|
||||
md-input-placeholder="新的昵称..."
|
||||
md-confirm-text="提交"
|
||||
md-cancel-text="取消"
|
||||
@md-confirm="modifyNick"
|
||||
/>
|
||||
<md-snackbar
|
||||
md-position="center"
|
||||
:md-active.sync="show_snackbar"
|
||||
md-persistent
|
||||
:md-duration="1500"
|
||||
>
|
||||
<span>{{ snakebar_msg }}</span>
|
||||
</md-snackbar>
|
||||
|
||||
<div :style="`height:60px`"></div>
|
||||
</md-app-content>
|
||||
</md-app>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// @ is an alias to /src
|
||||
import { mapState, mapActions } from "vuex";
|
||||
import { setHtmlFontSize } from "@/utils/px2rem.js";
|
||||
import { api } from "@/axios/fetch.js";
|
||||
import { encryptMainCode, decryptMainCode } from "@/utils/aes.js";
|
||||
|
||||
export default {
|
||||
name: "Account",
|
||||
data() {
|
||||
return {
|
||||
// 显示高度,控制md-app的最小高度,防止抽屉高度塌陷
|
||||
clientHeight: "",
|
||||
// snackbar控制
|
||||
show_snackbar: false,
|
||||
snakebar_msg: "",
|
||||
// 登录cid
|
||||
cid: "",
|
||||
// 登录pwd
|
||||
pwd: "",
|
||||
// 加载控制
|
||||
loading_switch: false,
|
||||
// 黑暗模式
|
||||
is_dark_mode: false,
|
||||
// vconsole
|
||||
open_vconsole: false,
|
||||
// 初始化完成状态
|
||||
has_init: false,
|
||||
// 退出登录confirm
|
||||
logout_confirm_switch: false,
|
||||
// 监听输入事件
|
||||
focus_list_1: 0,
|
||||
focus_list_2: 0,
|
||||
// 昵称输入弹窗
|
||||
nick_dialog_switch: false,
|
||||
// 新输入的昵称
|
||||
nick: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user_info", "settings"])
|
||||
},
|
||||
methods: {
|
||||
...mapActions(["setUserInfo", "setSettings", "setBarState"]),
|
||||
// 修改md-app的最小高度
|
||||
changeFixed(clientHeight) {
|
||||
//动态修改样式
|
||||
this.$refs.account.children[0].style.minHeight = clientHeight + "px";
|
||||
this.$refs.list_placeholder.parentNode.parentNode.style.maxHeight =
|
||||
clientHeight + "px";
|
||||
this.$refs.list_placeholder.parentNode.style.minHeight =
|
||||
clientHeight - 32 + "px";
|
||||
window.document.documentElement.setAttribute(
|
||||
"data-theme",
|
||||
this.settings.is_dark_mode ? "dark" : "light"
|
||||
);
|
||||
},
|
||||
|
||||
// 初始化
|
||||
init() {
|
||||
// 刷新vuex
|
||||
this.$store.replaceState(
|
||||
Object.assign(
|
||||
this.$store.state,
|
||||
JSON.parse(localStorage.getItem("storeState"))
|
||||
)
|
||||
);
|
||||
// 底部状态栏状态设置
|
||||
this.setBarState([true, this])
|
||||
// 初始化本页信息
|
||||
this.open_vconsole = this.settings.open_vconsole;
|
||||
this.is_dark_mode = this.settings.is_dark_mode;
|
||||
// 设置flag
|
||||
setTimeout(
|
||||
function() {
|
||||
this.has_init = true;
|
||||
console.log("本页信息覆写完成");
|
||||
}.bind(this),
|
||||
10
|
||||
);
|
||||
},
|
||||
|
||||
// 管理员登录
|
||||
login(is_refresh = false) {
|
||||
// 格式校验
|
||||
if (!/\d+/.test(this.cid)) {
|
||||
console.log("数据格式校验失败");
|
||||
this.message("请输入正确的教务账号");
|
||||
return;
|
||||
}
|
||||
if (!(this.pwd = this.pwd.trim())) {
|
||||
console.log("数据格式校验失败");
|
||||
this.message("请输入的教务密码");
|
||||
return;
|
||||
}
|
||||
this.loading_switch = true;
|
||||
let data = {
|
||||
cid: this.cid,
|
||||
pwd: this.pwd
|
||||
};
|
||||
console.log(`上传信息准备完成`);
|
||||
console.log(data);
|
||||
api
|
||||
.post("/login", data)
|
||||
.then(res => {
|
||||
let user_info = res.data.user_info;
|
||||
user_info.pwd = encryptMainCode(this.pwd);
|
||||
this.setUserInfo([user_info, this]);
|
||||
console.log("登录成功,用户信息覆写完成");
|
||||
let msg = is_refresh ? "刷新成功" : "登录成功";
|
||||
this.message(msg);
|
||||
this.loading_switch = false;
|
||||
this.cid = "";
|
||||
this.pwd = "";
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
this.loading_switch = false;
|
||||
if (err.response && err.response.status != 500) {
|
||||
this.message(`${err.response.status}: ${err.response.data}`);
|
||||
} else {
|
||||
this.message("网络错误,请稍候重试");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 刷新用户信息
|
||||
refresh() {
|
||||
if (Object.keys(this.user_info).length == 0) return;
|
||||
this.cid = this.user_info.cid;
|
||||
this.pwd = decryptMainCode(this.user_info.pwd);
|
||||
this.login(true);
|
||||
},
|
||||
|
||||
//覆写个性化设置
|
||||
resetSettings() {
|
||||
let settings = {
|
||||
open_vconsole: this.open_vconsole,
|
||||
is_dark_mode: this.is_dark_mode
|
||||
};
|
||||
this.setSettings([settings, this]);
|
||||
console.log("个性化设置覆写完成");
|
||||
// 设置黑暗模式
|
||||
window.document.documentElement.setAttribute(
|
||||
"data-theme",
|
||||
this.settings.is_dark_mode ? "dark" : "light"
|
||||
);
|
||||
// 设置vconsole
|
||||
this.toggleVcon();
|
||||
if(this.has_init)
|
||||
this.message("设置成功");
|
||||
},
|
||||
// 调用snackBar
|
||||
message(msg) {
|
||||
this.snakebar_msg = msg;
|
||||
this.show_snackbar = true;
|
||||
},
|
||||
|
||||
// 判断是否有class
|
||||
hasClass(obj, cls) {
|
||||
return obj.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)"));
|
||||
},
|
||||
// 增加class
|
||||
addClass(obj, cls) {
|
||||
if (!this.hasClass(obj, cls)) obj.className += " " + cls;
|
||||
},
|
||||
// 移除class
|
||||
removeClass(obj, cls) {
|
||||
if (this.hasClass(obj, cls)) {
|
||||
var reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
|
||||
obj.className = obj.className.replace(reg, " ");
|
||||
}
|
||||
},
|
||||
// 改变class
|
||||
toggleVcon() {
|
||||
let vcon_dom = document.getElementById("__vconsole");
|
||||
let class_name = "vconsole-show";
|
||||
if (this.hasClass(vcon_dom, class_name)) {
|
||||
// 当前显示
|
||||
if (!this.settings.open_vconsole) {
|
||||
// 设置不显示
|
||||
this.removeClass(vcon_dom, class_name);
|
||||
}
|
||||
} else {
|
||||
// 当前不显示
|
||||
if (this.settings.open_vconsole) {
|
||||
// 设置显示
|
||||
this.addClass(vcon_dom, class_name);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 退出登录
|
||||
logout() {
|
||||
this.setUserInfo([{}, this]);
|
||||
this.cid = "";
|
||||
this.pwd = "";
|
||||
console.log("用户信息覆写完成");
|
||||
this.message("本地信息清空完成");
|
||||
},
|
||||
|
||||
// 监听聚焦和脱焦事件控制bottomBar的显示和隐藏
|
||||
focusOrBlur() {
|
||||
let focus_list = [
|
||||
this.focus_list_1,
|
||||
this.focus_list_2,
|
||||
this.focus_list_3,
|
||||
this.focus_list_4
|
||||
];
|
||||
if (focus_list.indexOf(1) != -1) {
|
||||
this.setBarState([false, this]);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
let focus_list = [
|
||||
this.focus_list_1,
|
||||
this.focus_list_2,
|
||||
this.focus_list_3,
|
||||
this.focus_list_4
|
||||
];
|
||||
if (focus_list.indexOf(1) == -1) {
|
||||
this.setBarState([true, this]);
|
||||
}
|
||||
}, 150);
|
||||
}
|
||||
},
|
||||
|
||||
// 修改昵称
|
||||
modifyNick() {
|
||||
this.nick_dialog_switch = false
|
||||
this.loading_switch = true
|
||||
let data = {
|
||||
openid: this.user_info.openid,
|
||||
nick: this.nick
|
||||
}
|
||||
api
|
||||
.put("/nick", data)
|
||||
.then(res => {
|
||||
this.loading_switch = false;
|
||||
let user_info = this.user_info
|
||||
user_info.nick = this.nick
|
||||
this.setUserInfo([user_info, this]);
|
||||
console.log("用户信息覆写成功");
|
||||
this.message('修改成功')
|
||||
this.nick = ""
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
this.loading_switch = false;
|
||||
if (err.response && err.response.status != 500) {
|
||||
this.message(`${err.response.status}: ${err.response.data}`);
|
||||
} else {
|
||||
this.message("网络错误,请稍候重试");
|
||||
}
|
||||
this.nick = ""
|
||||
});
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
mounted() {
|
||||
// 获取浏览器可视区域高度
|
||||
this.clientHeight = `${document.documentElement.clientHeight}`;
|
||||
//document.body.clientWidth;
|
||||
window.onresize = function temp() {
|
||||
this.clientHeight = `${document.documentElement.clientHeight}`;
|
||||
setHtmlFontSize();
|
||||
}.bind(this);
|
||||
this.$refs.list_placeholder.parentNode.parentNode.addEventListener(
|
||||
"scroll",
|
||||
this.scroll
|
||||
);
|
||||
},
|
||||
watch: {
|
||||
// 如果 `clientHeight` 发生改变,这个函数就会运行
|
||||
clientHeight: function() {
|
||||
this.changeFixed(this.clientHeight);
|
||||
},
|
||||
// 如果 `is_dark_mode` 发生改变,就会执行设置函数
|
||||
is_dark_mode: function() {
|
||||
this.resetSettings();
|
||||
},
|
||||
// 如果 `open_vconsole` 发生改变,就会执行设置函数
|
||||
open_vconsole: function() {
|
||||
this.resetSettings();
|
||||
}
|
||||
},
|
||||
beforeDestroy() {},
|
||||
components: {}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss" type="text/scss">
|
||||
@import "../../style/main";
|
||||
.account {
|
||||
width: 100%;
|
||||
// max-width: 500px;
|
||||
min-height: 100%;
|
||||
background: #fff;
|
||||
// margin: 0 auto;
|
||||
.expand {
|
||||
box-sizing: border-box;
|
||||
min-height: 0.8rem !important;
|
||||
margin: 0.5rem 16px !important;
|
||||
}
|
||||
.login-submit {
|
||||
width: 100%;
|
||||
margin: 0.5rem 0 !important;
|
||||
}
|
||||
.md-toolbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
.md-toolbar-row {
|
||||
order: 12;
|
||||
// margin-bottom: .3rem !important;
|
||||
}
|
||||
.md-toolbar-section-start,
|
||||
.md-toolbar-section-end,
|
||||
.md-toolbar-row {
|
||||
min-height: 56px;
|
||||
}
|
||||
.md-toolbar-row {
|
||||
margin-top: -10px;
|
||||
}
|
||||
.md-speed-dial.md-bottom-right {
|
||||
position: fixed !important;
|
||||
}
|
||||
}
|
||||
// Demo purposes only
|
||||
.md-drawer {
|
||||
width: 240px;
|
||||
max-width: calc(100vw - 125px);
|
||||
}
|
||||
.md-app-scroller > div {
|
||||
overflow-y: scroll !important;
|
||||
}
|
||||
.face {
|
||||
color: #fff !important;
|
||||
}
|
||||
.page-title {
|
||||
flex: 1;
|
||||
min-height: 56px;
|
||||
line-height: 56px;
|
||||
}
|
||||
.sroll-top-area {
|
||||
flex: 1;
|
||||
min-height: 56px;
|
||||
}
|
||||
.md-bottom-bar.md-type-fixed .md-bottom-bar-item {
|
||||
max-width: 1000px !important;
|
||||
}
|
||||
</style>
|
410
src/views/Create/Create.vue
Normal file
@ -0,0 +1,410 @@
|
||||
<template>
|
||||
<div class="create" ref="create">
|
||||
<md-app md-waterfall md-mode="fixed">
|
||||
<md-app-toolbar class="md-primary">
|
||||
<div class="md-toolbar-section-start">
|
||||
<span class="md-title page-title">创建失物招领</span>
|
||||
</div>
|
||||
<div class="md-toolbar-section-end">
|
||||
<div class="sroll-top-area"></div>
|
||||
</div>
|
||||
</md-app-toolbar>
|
||||
<md-app-content>
|
||||
<div ref="list_placeholder" :style="`height:56px`"></div>
|
||||
<md-progress-bar
|
||||
v-if="loading_switch"
|
||||
md-mode="indeterminate"
|
||||
style="position: absolute; top: 56px; left: 0; width: 100%;"
|
||||
></md-progress-bar>
|
||||
|
||||
<md-content>
|
||||
<md-radio class="md-primary" v-model="type" value="lost">丢了东西</md-radio>
|
||||
<md-radio class="md-primary" v-model="type" value="found">捡到东西</md-radio>
|
||||
</md-content>
|
||||
<md-field style="margin-top: -10px;">
|
||||
<label>标题*</label>
|
||||
<md-input
|
||||
v-model="title"
|
||||
maxlength="15"
|
||||
id="title"
|
||||
@focus="focus_list_1 = 1;focusOrBlur()"
|
||||
@blur="focus_list_1 = 0;focusOrBlur()"
|
||||
></md-input>
|
||||
<span class="md-helper-text">譬如:水杯、手机等易于搜索的~</span>
|
||||
</md-field>
|
||||
<md-field style="margin-top:40px;">
|
||||
<label>描述</label>
|
||||
<md-textarea
|
||||
v-model="content"
|
||||
maxlength="100"
|
||||
@focus="focus_list_2 = 1;focusOrBlur()"
|
||||
@blur="focus_list_2 = 0;focusOrBlur()"
|
||||
></md-textarea>
|
||||
<span class="md-helper-text">描述一下它的特征之类的</span>
|
||||
</md-field>
|
||||
<md-field style="margin-top:40px;">
|
||||
<label for="total_addr">地区*</label>
|
||||
<md-select v-model="total_addr" name="total_addr" id="total_addr">
|
||||
<md-option value="长理东区">长理东区</md-option>
|
||||
<md-option value="长理南区">长理南区</md-option>
|
||||
<md-option value="长理西区">长理西区</md-option>
|
||||
</md-select>
|
||||
</md-field>
|
||||
<md-field style="margin-top: -10px;">
|
||||
<label>具体位置*</label>
|
||||
<md-input
|
||||
v-model="detail_addr"
|
||||
maxlength="30"
|
||||
@focus="focus_list_3 = 1;focusOrBlur()"
|
||||
@blur="focus_list_3 = 0;focusOrBlur()"
|
||||
></md-input>
|
||||
<span class="md-helper-text">譬如:南研楼1201阶梯教室第四排~</span>
|
||||
</md-field>
|
||||
<md-field>
|
||||
<label>联系方式</label>
|
||||
<md-input
|
||||
v-model="contact"
|
||||
maxlength="20"
|
||||
@focus="focus_list_4 = 1;focusOrBlur()"
|
||||
@blur="focus_list_4 = 0;focusOrBlur()"
|
||||
></md-input>
|
||||
<span class="md-helper-text">譬如:手机号,微信号,qq号(非必填项</span>
|
||||
</md-field>
|
||||
<md-field style="margin-top:40px;">
|
||||
<label>图片上传</label>
|
||||
<md-file v-model="upload_file" accept="image/*" @md-change="processFile"/>
|
||||
</md-field>
|
||||
<md-button
|
||||
class="md-dense md-raised md-primary expand"
|
||||
@click="judge()"
|
||||
:disable="!loading_switch"
|
||||
>确认提交</md-button>
|
||||
<md-dialog-confirm
|
||||
:md-active.sync="submit_confirm_switch"
|
||||
md-title="提示"
|
||||
md-content="该信息即将提交审核,是否继续"
|
||||
md-confirm-text="继续"
|
||||
md-cancel-text="取消"
|
||||
@md-confirm="uploadPhoto"
|
||||
:md-click-outside-to-close="false"
|
||||
/>
|
||||
<md-dialog-alert
|
||||
:md-active.sync="error_alert_switch"
|
||||
md-title="警告"
|
||||
md-content="请检查您所填写的内容</br>除了联系方式以及图片皆为<strong>必填项</strong></br>还请注意<strong>输入长度</strong>"
|
||||
md-confirm-text="我知道了"/>
|
||||
|
||||
<md-snackbar
|
||||
md-position="center"
|
||||
:md-active.sync="show_snackbar"
|
||||
md-persistent
|
||||
:md-duration="1500"
|
||||
>
|
||||
<span>{{ snakebar_msg }}</span>
|
||||
</md-snackbar>
|
||||
<div :style="`height:86px`"></div>
|
||||
</md-app-content>
|
||||
</md-app>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// @ is an alias to /src
|
||||
import { mapState, mapActions } from "vuex";
|
||||
import { setHtmlFontSize } from "@/utils/px2rem.js";
|
||||
import { api } from "@/axios/fetch.js";
|
||||
import "lrz";
|
||||
|
||||
export default {
|
||||
name: "Create",
|
||||
data() {
|
||||
return {
|
||||
// 显示高度,控制md-app的最小高度,防止抽屉高度塌陷
|
||||
clientHeight: "",
|
||||
// snackbar控制
|
||||
show_snackbar: false,
|
||||
snakebar_msg: "",
|
||||
// 类型
|
||||
type: "lost",
|
||||
// 标题
|
||||
title: "",
|
||||
// 内容
|
||||
content: "",
|
||||
// 地区
|
||||
total_addr: "长理东区",
|
||||
// 详细地址
|
||||
detail_addr: "",
|
||||
// 联系方式
|
||||
contact: "",
|
||||
// 图片
|
||||
upload_file: null,
|
||||
// 压缩后图片
|
||||
lrz_file: null,
|
||||
// 加载控制
|
||||
loading_switch: false,
|
||||
// 监听输入事件
|
||||
focus_list_1: 0,
|
||||
focus_list_2: 0,
|
||||
focus_list_3: 0,
|
||||
focus_list_4: 0,
|
||||
// 确认提交框
|
||||
submit_confirm_switch: false,
|
||||
// 错误提醒框
|
||||
error_alert_switch: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user_info", "settings"])
|
||||
},
|
||||
methods: {
|
||||
...mapActions(["setUserInfo", "setSettings", "setBarState"]),
|
||||
|
||||
// 修改md-app的最小高度
|
||||
changeFixed(clientHeight) {
|
||||
//动态修改样式
|
||||
this.$refs.create.children[0].style.minHeight = clientHeight + "px";
|
||||
this.$refs.list_placeholder.parentNode.parentNode.style.maxHeight =
|
||||
clientHeight + "px";
|
||||
this.$refs.list_placeholder.parentNode.style.minHeight =
|
||||
clientHeight - 32 + "px";
|
||||
window.document.documentElement.setAttribute(
|
||||
"data-theme",
|
||||
this.settings.is_dark_mode ? "dark" : "light"
|
||||
);
|
||||
},
|
||||
|
||||
// 初始化
|
||||
init() {
|
||||
// 刷新vuex
|
||||
this.$store.replaceState(
|
||||
Object.assign(
|
||||
this.$store.state,
|
||||
JSON.parse(localStorage.getItem("storeState"))
|
||||
)
|
||||
);
|
||||
// 底部状态栏状态设置
|
||||
this.setBarState([true, this])
|
||||
},
|
||||
|
||||
// 调用snackBar
|
||||
message(msg) {
|
||||
this.snakebar_msg = msg;
|
||||
this.show_snackbar = true;
|
||||
},
|
||||
|
||||
// 监听聚焦和脱焦事件控制bottomBar的显示和隐藏
|
||||
focusOrBlur() {
|
||||
let focus_list = [this.focus_list_1, this.focus_list_2, this.focus_list_3, this.focus_list_4]
|
||||
if (focus_list.indexOf(1) != -1) {
|
||||
this.setBarState([false, this]);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
let focus_list = [this.focus_list_1, this.focus_list_2, this.focus_list_3, this.focus_list_4]
|
||||
if (focus_list.indexOf(1) == -1) {
|
||||
this.setBarState([true, this]);
|
||||
}
|
||||
}, 150);
|
||||
}
|
||||
},
|
||||
|
||||
// 用户点击上传校验输入信息
|
||||
judge() {
|
||||
if(Object.keys(this.user_info).length == 0) {
|
||||
this.message('请先登录')
|
||||
return
|
||||
}
|
||||
let flag = true
|
||||
this.title = this.title.trim()
|
||||
if(!this.title || this.title.length > 15) flag = false
|
||||
this.content = this.content.trim()
|
||||
if(!this.content || this.content.length > 100) flag = false
|
||||
this.detail_addr = this.detail_addr.trim()
|
||||
if(!this.detail_addr || this.detail_addr.length > 30) flag = false
|
||||
this.contact = this.contact.trim()
|
||||
flag ? this.submit_confirm_switch = true : this.error_alert_switch = true
|
||||
},
|
||||
|
||||
// 准备图片上传
|
||||
uploadPhoto() {
|
||||
this.loading_switch = true;
|
||||
if(!this.lrz_file) this.uploadData('default.jpg')
|
||||
let param = new FormData();
|
||||
param.append('photo', this.lrz_file);
|
||||
api.put('/upload', param, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
}
|
||||
}).then(res => {
|
||||
this.uploadData(res.data.file_name)
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
this.loading_switch = false;
|
||||
if(err.response && err.response.status != 500) {
|
||||
this.message(`${err.response.status}: ${err.response.data}`)
|
||||
} else {
|
||||
this.message('网络错误,请稍候重试')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 上传信息
|
||||
uploadData(file_name) {
|
||||
console.log('文件上传成功,文件名:', file_name)
|
||||
// 组织信息
|
||||
let data = {
|
||||
title: this.title,
|
||||
content: this.content,
|
||||
create_time: new Date().getTime(),
|
||||
img_url: file_name,
|
||||
total_addr: this.total_addr,
|
||||
detail_addr: this.detail_addr,
|
||||
contact: this.contact,
|
||||
type: this.type,
|
||||
user_info: {
|
||||
openid: this.user_info.openid,
|
||||
nick: this.user_info.nick
|
||||
}
|
||||
}
|
||||
api.post('/add', data).then(res => {
|
||||
console.log('上传成功')
|
||||
this.loading_switch = false;
|
||||
this.message('上传成功,请等待审核')
|
||||
this.initData()
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
this.loading_switch = false;
|
||||
if(err.response && err.response.status != 500) {
|
||||
this.message(`${err.response.status}: ${err.response.data}`)
|
||||
} else {
|
||||
this.message('网络错误,请稍候重试')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 上传成功初始化本页信息
|
||||
initData() {
|
||||
this.type = "lost";
|
||||
this.title = '';
|
||||
this.content = '';
|
||||
this.total_addr = "长理东区";
|
||||
this.detail_addr = ""
|
||||
this.contact = ""
|
||||
this.upload_file = null
|
||||
this.lrz_file = null
|
||||
console.log('创建信息初始化完成')
|
||||
},
|
||||
|
||||
// 处理图片文件
|
||||
processFile: async function(e) {
|
||||
let file = e[0]
|
||||
if (file) {
|
||||
let name = file.name
|
||||
console.log('文件压缩开始')
|
||||
file = await lrz(file);
|
||||
file = file.file;
|
||||
file = new File([file], name);
|
||||
this.lrz_file = file;
|
||||
console.log('文件压缩完成', this.lrz_file)
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
mounted() {
|
||||
// 获取浏览器可视区域高度
|
||||
this.clientHeight = `${document.documentElement.clientHeight}`;
|
||||
//document.body.clientWidth;
|
||||
window.onresize = function temp() {
|
||||
this.clientHeight = `${document.documentElement.clientHeight}`;
|
||||
setHtmlFontSize();
|
||||
}.bind(this);
|
||||
this.$refs.list_placeholder.parentNode.parentNode.addEventListener(
|
||||
"scroll",
|
||||
this.scroll
|
||||
);
|
||||
// document.getElementById("title")
|
||||
},
|
||||
watch: {
|
||||
// 如果 `clientHeight` 发生改变,这个函数就会运行
|
||||
clientHeight: function() {
|
||||
this.changeFixed(this.clientHeight);
|
||||
},
|
||||
// upload_file: async function() {
|
||||
// if(typeof(this.upload_file) == 'object') return
|
||||
// console.log(this.upload_file)
|
||||
// let file = this.upload_file
|
||||
// if (file) {
|
||||
// let name = file.name
|
||||
// console.log('文件压缩开始')
|
||||
// file = await lrz(file);
|
||||
// file = file.file;
|
||||
// file = new File([file], name);
|
||||
// this.lrz_file = file;
|
||||
// console.log('文件压缩完成', this.lrz_file)
|
||||
// }
|
||||
// }
|
||||
},
|
||||
beforeDestroy() {},
|
||||
components: {}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss" type="text/scss">
|
||||
@import "../../style/main";
|
||||
.create {
|
||||
width: 100%;
|
||||
// max-width: 500px;
|
||||
min-height: 100%;
|
||||
background: #fff;
|
||||
// margin: 0 auto;
|
||||
.expand {
|
||||
width: 100%;
|
||||
margin: 0 !important;
|
||||
min-height: 0.8rem !important;
|
||||
margin-top: 0.5rem !important;
|
||||
}
|
||||
.md-toolbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
.md-toolbar-row {
|
||||
order: 12;
|
||||
// margin-bottom: .3rem !important;
|
||||
}
|
||||
.md-toolbar-section-start,
|
||||
.md-toolbar-section-end,
|
||||
.md-toolbar-row {
|
||||
min-height: 56px;
|
||||
}
|
||||
.md-toolbar-row {
|
||||
margin-top: -10px;
|
||||
}
|
||||
.md-speed-dial.md-bottom-right {
|
||||
position: fixed !important;
|
||||
}
|
||||
}
|
||||
// Demo purposes only
|
||||
.md-drawer {
|
||||
width: 240px;
|
||||
max-width: calc(100vw - 125px);
|
||||
}
|
||||
.md-app-scroller > div {
|
||||
overflow-y: scroll !important;
|
||||
}
|
||||
.face {
|
||||
color: #fff !important;
|
||||
}
|
||||
.page-title {
|
||||
flex: 1;
|
||||
min-height: 56px;
|
||||
line-height: 56px;
|
||||
}
|
||||
.sroll-top-area {
|
||||
flex: 1;
|
||||
min-height: 56px;
|
||||
}
|
||||
.md-bottom-bar.md-type-fixed .md-bottom-bar-item {
|
||||
max-width: 1000px !important;
|
||||
}
|
||||
</style>
|
483
src/views/Detail/Detail.vue
Normal file
@ -0,0 +1,483 @@
|
||||
<template>
|
||||
<div class="detail" ref="detail">
|
||||
<md-app md-waterfall md-mode="fixed">
|
||||
<md-app-toolbar class="md-primary">
|
||||
<div class="md-toolbar-section-start">
|
||||
<md-button class="md-icon-button" @click="back">
|
||||
<md-icon>arrow_back</md-icon>
|
||||
</md-button>
|
||||
<span class="md-title page-title">曳光 - 详情</span>
|
||||
</div>
|
||||
<div class="md-toolbar-section-end">
|
||||
<md-button
|
||||
class="md-icon-button"
|
||||
@click="comment_dialog_switch = true; is_comment = true"
|
||||
v-if="Object.keys(user_info).length != 0 && content && content.close == false"
|
||||
>
|
||||
<md-icon>rate_review</md-icon>
|
||||
</md-button>
|
||||
<md-button
|
||||
class="md-icon-button"
|
||||
v-if="Object.keys(user_info).length != 0 && content && user_info.openid == content.user_info.openid && content.close == false"
|
||||
@click="delete_confirm_switch = true"
|
||||
>
|
||||
<md-icon>delete</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
</md-app-toolbar>
|
||||
<md-app-content>
|
||||
<v-touch
|
||||
@swiperight="back"
|
||||
:swipe-options="{ direction: 'horizontal' }"
|
||||
>
|
||||
<div ref="list_placeholder" :style="`height:56px`"></div>
|
||||
<md-progress-bar
|
||||
v-if="loading_switch"
|
||||
md-mode="indeterminate"
|
||||
style="position: absolute; top: 56px; left: 0; width: 100%;"
|
||||
></md-progress-bar>
|
||||
<icon class="loading" name="canary" v-if="!content"></icon>
|
||||
<md-card class="md-card-example" v-else>
|
||||
<md-card-area md-inset>
|
||||
<md-card-media>
|
||||
<img :src="`https://yg.canary.moe/photo/${content.img_url}`" />
|
||||
</md-card-media>
|
||||
|
||||
<md-card-header>
|
||||
<h2 class="md-title" style="margin-bottom: .3rem;">{{content.title}}</h2>
|
||||
<div class="md-subhead">
|
||||
<md-icon>access_time</md-icon>
|
||||
<span>{{formatDateTime(content.create_time)}}</span>
|
||||
</div>
|
||||
<div class="md-subhead">
|
||||
<md-icon>location_on</md-icon>
|
||||
<span>{{content.total_addr}}</span>
|
||||
</div>
|
||||
<div class="md-subhead">
|
||||
<md-icon>gps_fixed</md-icon>
|
||||
<span>{{content.detail_addr}}</span>
|
||||
</div>
|
||||
<div class="md-subhead" v-if="content.contact">
|
||||
<md-icon>attach_file</md-icon>
|
||||
<span>{{content.contact}}</span>
|
||||
</div>
|
||||
</md-card-header>
|
||||
<md-card-content>{{content.content}}</md-card-content>
|
||||
</md-card-area>
|
||||
</md-card>
|
||||
|
||||
<md-list
|
||||
class="md-double-line md-dense"
|
||||
style="margin-top: .3rem;"
|
||||
v-if="content && Object.keys(content.comment).length != 0"
|
||||
>
|
||||
<md-subheader class="md-primary">评论区</md-subheader>
|
||||
<div v-for="(comment, index0) in content.comment" :key="index0">
|
||||
<md-list-item>
|
||||
<div class="md-list-item-text">
|
||||
<span>{{comment.user_info.nick}}</span>
|
||||
<p>{{comment.content}}</p>
|
||||
<p>{{formatDateTime(comment.create_time)}}</p>
|
||||
</div>
|
||||
<md-button
|
||||
class="md-icon-button md-list-action"
|
||||
@click="targetReply(index0, comment.user_info.nick)"
|
||||
v-if="Object.keys(user_info).length != 0 && content.close == false"
|
||||
>
|
||||
<md-icon>rate_review</md-icon>
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
<div v-for="(reply, index1) in comment.reply" :key="index1">
|
||||
<md-list-item>
|
||||
<md-icon class="md-primary">keyboard_arrow_right</md-icon>
|
||||
<div class="md-list-item-text">
|
||||
<span>{{reply.user_info.nick}}</span>
|
||||
<p>{{reply.content}}</p>
|
||||
<span>{{formatDateTime(reply.create_time)}}</span>
|
||||
</div>
|
||||
<md-button
|
||||
class="md-icon-button md-list-action"
|
||||
@click="targetReply(index0, reply.user_info.nick)"
|
||||
v-if="Object.keys(user_info).length != 0 && content.close == false"
|
||||
>
|
||||
<md-icon>rate_review</md-icon>
|
||||
</md-button>
|
||||
</md-list-item>
|
||||
</div>
|
||||
<md-divider style="margin-top: .3rem;margin-bottom: .3rem;"></md-divider>
|
||||
</div>
|
||||
</md-list>
|
||||
|
||||
<md-dialog-confirm
|
||||
:md-active.sync="delete_confirm_switch"
|
||||
md-title="警告"
|
||||
md-content="您将关闭该条记录</br>该操作不可复原,是否继续"
|
||||
md-confirm-text="继续"
|
||||
md-cancel-text="取消"
|
||||
:md-click-outside-to-close="false"
|
||||
@md-confirm="closeLaf"
|
||||
/>
|
||||
<md-dialog-prompt
|
||||
:md-active.sync="comment_dialog_switch"
|
||||
v-model="comment"
|
||||
:md-title="is_comment ? '评论': '回复'"
|
||||
md-input-maxlength="30"
|
||||
:md-input-placeholder="`${is_comment ? '评论': '回复'}的内容...`"
|
||||
md-confirm-text="提交"
|
||||
md-cancel-text="取消"
|
||||
@md-confirm="commentSubmit"
|
||||
@md-cancel="commentCancel"
|
||||
/>
|
||||
<md-snackbar
|
||||
md-position="center"
|
||||
:md-active.sync="show_snackbar"
|
||||
md-persistent
|
||||
:md-duration="1500"
|
||||
>
|
||||
<span>{{ snakebar_msg }}</span>
|
||||
</md-snackbar>
|
||||
<div style="height: 40px;"></div>
|
||||
</v-touch>
|
||||
</md-app-content>
|
||||
</md-app>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// @ is an alias to /src
|
||||
import { mapState, mapActions } from "vuex";
|
||||
import { setHtmlFontSize } from "@/utils/px2rem.js";
|
||||
import { api } from "@/axios/fetch.js";
|
||||
import { encryptMainCode, decryptMainCode } from "@/utils/aes.js";
|
||||
import { formatDateTime } from "@/utils/formatTime.js";
|
||||
|
||||
export default {
|
||||
name: "Detail",
|
||||
data() {
|
||||
return {
|
||||
// 显示高度,控制md-app的最小高度,防止抽屉高度塌陷
|
||||
clientHeight: "",
|
||||
// snackbar控制
|
||||
show_snackbar: false,
|
||||
snakebar_msg: "",
|
||||
// 页面内容
|
||||
content: null,
|
||||
// 加载控制
|
||||
loading_switch: false,
|
||||
// 删除确认弹窗控制
|
||||
delete_confirm_switch: false,
|
||||
// 提交评论
|
||||
comment_dialog_switch: false,
|
||||
comment: "",
|
||||
is_comment: true, // 'reply' => false
|
||||
// 回复数据暂存
|
||||
reply_cache: null
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user_info", "settings",'bar_state'])
|
||||
},
|
||||
methods: {
|
||||
...mapActions(["setUserInfo", "setSettings", "setBarState"]),
|
||||
formatDateTime,
|
||||
// 修改md-app的最小高度
|
||||
changeFixed(clientHeight) {
|
||||
//动态修改样式
|
||||
this.$refs.detail.children[0].style.minHeight = clientHeight + "px";
|
||||
this.$refs.list_placeholder.parentNode.parentNode.style.maxHeight =
|
||||
clientHeight + "px";
|
||||
this.$refs.list_placeholder.parentNode.style.minHeight =
|
||||
clientHeight - 32 + "px";
|
||||
window.document.documentElement.setAttribute(
|
||||
"data-theme",
|
||||
this.settings.is_dark_mode ? "dark" : "light"
|
||||
);
|
||||
},
|
||||
|
||||
// 初始化
|
||||
init() {
|
||||
// 刷新vuex
|
||||
this.$store.replaceState(
|
||||
Object.assign(
|
||||
this.$store.state,
|
||||
JSON.parse(localStorage.getItem("storeState"))
|
||||
)
|
||||
);
|
||||
// 底部状态栏状态设置
|
||||
this.setBarState([false, this]);
|
||||
// 获取id
|
||||
let id = this.$route.query.id;
|
||||
// 格式校验
|
||||
if (/\d{16}/.test(id)) {
|
||||
this.getDetail(id);
|
||||
} else {
|
||||
this.back();
|
||||
}
|
||||
},
|
||||
|
||||
// 返回到主页
|
||||
back() {
|
||||
this.$router.replace("/home");
|
||||
},
|
||||
|
||||
// 获取详细信息
|
||||
getDetail(id) {
|
||||
let url = `/detail?id=${id}`;
|
||||
this.loading_switch = true;
|
||||
api
|
||||
.get(url)
|
||||
.then(res => {
|
||||
this.loading_switch = false;
|
||||
this.content = res.data.content;
|
||||
console.log(this.content);
|
||||
console.log("信息获取成功,详情覆写成功");
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
this.loading_switch = false;
|
||||
if (err.response && err.response.status != 500) {
|
||||
this.message(`${err.response.status}: ${err.response.data}`);
|
||||
} else {
|
||||
this.message("网络错误,请稍候重试");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 调用snackBar
|
||||
message(msg) {
|
||||
this.snakebar_msg = msg;
|
||||
this.show_snackbar = true;
|
||||
},
|
||||
|
||||
targetReply(commentid, reply_nick) {
|
||||
this.reply_cache = {
|
||||
commentid,
|
||||
reply_nick
|
||||
};
|
||||
this.is_comment = false;
|
||||
this.comment_dialog_switch = true;
|
||||
},
|
||||
|
||||
// 评论
|
||||
commentSubmit() {
|
||||
this.loading_switch = true;
|
||||
this.comment = this.comment.trim();
|
||||
if (!this.comment || this.comment.length > 30) {
|
||||
console.log("内容校验失败");
|
||||
this.message("请确保字数在1到30之间");
|
||||
this.comment = "";
|
||||
return;
|
||||
}
|
||||
let data;
|
||||
if (this.is_comment) {
|
||||
data = {
|
||||
content: {
|
||||
user_info: {
|
||||
openid: this.user_info.openid,
|
||||
nick: this.user_info.nick
|
||||
},
|
||||
content: this.comment,
|
||||
create_time: new Date().getTime()
|
||||
},
|
||||
position: {
|
||||
is_comment: true,
|
||||
lafid: this.content.id,
|
||||
openid: this.content.user_info.openid
|
||||
}
|
||||
};
|
||||
} else {
|
||||
data = {
|
||||
content: {
|
||||
user_info: {
|
||||
openid: this.user_info.openid,
|
||||
nick: this.user_info.nick
|
||||
},
|
||||
content: `@${this.reply_cache.reply_nick}: ${this.comment}`,
|
||||
create_time: new Date().getTime()
|
||||
},
|
||||
position: {
|
||||
is_comment: false,
|
||||
lafid: this.content.id,
|
||||
openid: this.content.user_info.openid,
|
||||
commentid: this.reply_cache.commentid
|
||||
}
|
||||
};
|
||||
}
|
||||
console.log("上传信息准备完成");
|
||||
console.log(data);
|
||||
api
|
||||
.post("/comment", data)
|
||||
.then(res => {
|
||||
let msg = this.is_comment ? "评论成功" : "回复成功";
|
||||
console.log(msg);
|
||||
this.loading_switch = false;
|
||||
this.reply_cache = null;
|
||||
this.is_comment = true;
|
||||
this.comment = "";
|
||||
this.message(msg);
|
||||
this.getDetail(this.content.id);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
this.loading_switch = false;
|
||||
if (err.response && err.response.status != 500) {
|
||||
this.message(`${err.response.status}: ${err.response.data}`);
|
||||
} else {
|
||||
this.message("网络错误,请稍候重试");
|
||||
}
|
||||
});
|
||||
},
|
||||
// 取消输入
|
||||
commentCancel() {
|
||||
this.reply_cache = null;
|
||||
this.is_comment = true;
|
||||
this.comment = "";
|
||||
},
|
||||
// 关闭失物招领
|
||||
closeLaf() {
|
||||
let data = {
|
||||
openid: this.user_info.openid,
|
||||
id: this.content.id
|
||||
};
|
||||
console.log("上传信息准备完成");
|
||||
console.log(data);
|
||||
api
|
||||
.delete("/close", { data })
|
||||
.then(res => {
|
||||
this.message("关闭成功");
|
||||
this.getDetail(this.content.id);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
this.loading_switch = false;
|
||||
if (err.response && err.response.status != 500) {
|
||||
this.message(`${err.response.status}: ${err.response.data}`);
|
||||
} else {
|
||||
this.message("网络错误,请稍候重试");
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
mounted() {
|
||||
// 获取浏览器可视区域高度
|
||||
this.clientHeight = `${document.documentElement.clientHeight}`;
|
||||
//document.body.clientWidth;
|
||||
window.onresize = function temp() {
|
||||
this.clientHeight = `${document.documentElement.clientHeight}`;
|
||||
setHtmlFontSize();
|
||||
}.bind(this);
|
||||
this.$refs.list_placeholder.parentNode.parentNode.addEventListener(
|
||||
"scroll",
|
||||
this.scroll
|
||||
);
|
||||
},
|
||||
watch: {
|
||||
// 如果 `clientHeight` 发生改变,这个函数就会运行
|
||||
clientHeight: function() {
|
||||
this.changeFixed(this.clientHeight);
|
||||
}
|
||||
},
|
||||
beforeDestroy() {},
|
||||
components: {}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss" type="text/scss">
|
||||
@import "../../style/main";
|
||||
.detail {
|
||||
width: 100%;
|
||||
// max-width: 500px;
|
||||
min-height: 100%;
|
||||
background: #fff;
|
||||
// margin: 0 auto;
|
||||
.expand {
|
||||
box-sizing: border-box;
|
||||
min-height: 0.8rem !important;
|
||||
margin: 0.5rem 16px !important;
|
||||
}
|
||||
.login-submit {
|
||||
width: 100%;
|
||||
margin: 0.5rem 0 !important;
|
||||
}
|
||||
.md-toolbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
.md-toolbar-row {
|
||||
order: 12;
|
||||
// margin-bottom: .3rem !important;
|
||||
}
|
||||
.md-toolbar-section-start,
|
||||
.md-toolbar-section-end,
|
||||
.md-toolbar-row {
|
||||
min-height: 56px;
|
||||
}
|
||||
.md-toolbar-row {
|
||||
margin-top: -10px;
|
||||
}
|
||||
.md-speed-dial.md-bottom-right {
|
||||
position: fixed !important;
|
||||
}
|
||||
}
|
||||
// Demo purposes only
|
||||
.md-drawer {
|
||||
width: 240px;
|
||||
max-width: calc(100vw - 125px);
|
||||
}
|
||||
.md-app-scroller > div {
|
||||
overflow-y: scroll !important;
|
||||
}
|
||||
.face {
|
||||
color: #fff !important;
|
||||
}
|
||||
.page-title {
|
||||
flex: 1;
|
||||
min-height: 56px;
|
||||
line-height: 56px;
|
||||
}
|
||||
.sroll-top-area {
|
||||
flex: 1;
|
||||
min-height: 56px;
|
||||
}
|
||||
.md-bottom-bar.md-type-fixed .md-bottom-bar-item {
|
||||
max-width: 1000px !important;
|
||||
}
|
||||
.md-card-example {
|
||||
.md-subhead {
|
||||
margin-top: 0.1rem;
|
||||
.md-icon {
|
||||
$size: 16px;
|
||||
width: $size;
|
||||
min-width: $size;
|
||||
height: $size;
|
||||
font-size: $size !important;
|
||||
margin-right: 0.3rem;
|
||||
}
|
||||
|
||||
span {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
.md-app-content .md-card {
|
||||
margin: 0 !important;
|
||||
}
|
||||
.loading {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
height: 5rem;
|
||||
width: 5rem;
|
||||
margin-top: -2.5rem;
|
||||
margin-left: -2.5rem;
|
||||
}
|
||||
.md-list-item-text p {
|
||||
word-wrap: break-word !important;
|
||||
overflow: visible !important;
|
||||
white-space: normal !important;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
</style>
|
481
src/views/Home/Home.vue
Normal file
@ -0,0 +1,481 @@
|
||||
<template>
|
||||
<div class="home" ref="home">
|
||||
<md-app md-mode="fixed">
|
||||
<md-app-toolbar class="md-primary">
|
||||
<div class="md-toolbar-section-start">
|
||||
<span class="md-title page-title">曳光</span>
|
||||
</div>
|
||||
<md-tabs
|
||||
class="md-primary"
|
||||
style="flex: 1"
|
||||
md-alignment="centered"
|
||||
:md-active-tab="page_type"
|
||||
>
|
||||
<md-tab id="lost" md-label="失物" @click="page_type = 'lost'"></md-tab>
|
||||
<md-tab id="found" md-label="招领" @click="page_type = 'found'"></md-tab>
|
||||
</md-tabs>
|
||||
<div class="md-toolbar-section-end">
|
||||
<div class="sroll-top-area"></div>
|
||||
<md-button class="md-icon-button" @click="switchSearch()">
|
||||
<md-icon>search</md-icon>
|
||||
</md-button>
|
||||
</div>
|
||||
<div class="md-toolbar-row" v-if="search_start">
|
||||
<md-autocomplete
|
||||
class="search"
|
||||
v-model="search_content"
|
||||
:md-options="current_title"
|
||||
md-layout="box"
|
||||
>
|
||||
<label>检索标题</label>
|
||||
</md-autocomplete>
|
||||
</div>
|
||||
</md-app-toolbar>
|
||||
<md-app-content>
|
||||
<v-touch
|
||||
@swiperight="page_type = 'lost'"
|
||||
@swipeleft="page_type = 'found'"
|
||||
:swipe-options="{ direction: 'horizontal' }"
|
||||
>
|
||||
<div ref="list_placeholder" :style="`height:${search_start ? '102' : '56'}px`"></div>
|
||||
|
||||
<md-progress-bar
|
||||
v-if="loading_switch"
|
||||
md-mode="indeterminate"
|
||||
:style="`position: absolute; top: ${search_start ? '102' : '56'}px; left: 0; width: 100%;`"
|
||||
></md-progress-bar>
|
||||
<template v-if="page_type == 'lost'">
|
||||
<!-- 空内容 -->
|
||||
<md-empty-state
|
||||
v-if="show_list.length == 0"
|
||||
class="md-primary"
|
||||
md-icon="done"
|
||||
md-label="暂无内容"
|
||||
md-description="当前并没有失物内容,或许你可以刷新一下"
|
||||
></md-empty-state>
|
||||
<template v-else>
|
||||
<md-content
|
||||
v-for="(item, index) in show_list"
|
||||
:key="index"
|
||||
@click="turnToDetail(item.id)"
|
||||
>
|
||||
<md-card class="md-card-example">
|
||||
<md-card-area md-inset>
|
||||
<md-card-media md-ratio="16:9">
|
||||
<img :src="`https://yg.canary.moe/photo/${item.img_url}`" />
|
||||
</md-card-media>
|
||||
|
||||
<md-card-header>
|
||||
<h2 class="md-subheading">{{item.title}}</h2>
|
||||
<div class="md-subhead">
|
||||
<md-icon>access_time</md-icon>
|
||||
<span>{{formatDateTime(item.create_time)}}</span>
|
||||
</div>
|
||||
<div class="md-subhead">
|
||||
<md-icon>location_on</md-icon>
|
||||
<span>{{item.total_addr}}</span>
|
||||
</div>
|
||||
</md-card-header>
|
||||
</md-card-area>
|
||||
</md-card>
|
||||
</md-content>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="page_type == 'found'">
|
||||
<!-- 空内容 -->
|
||||
<md-empty-state
|
||||
v-if="show_list.length == 0"
|
||||
class="md-primary"
|
||||
md-icon="done"
|
||||
md-label="暂无内容"
|
||||
md-description="当前并没有招领内容,或许你可以刷新一下"
|
||||
></md-empty-state>
|
||||
<template v-else>
|
||||
<md-content
|
||||
v-for="(item, index) in show_list"
|
||||
:key="index"
|
||||
@click="turnToDetail(item.id)"
|
||||
>
|
||||
<md-card class="md-card-example">
|
||||
<md-card-area md-inset>
|
||||
<md-card-media md-ratio="16:9">
|
||||
<img :src="`https://yg.canary.moe/photo/${item.img_url}`" />
|
||||
</md-card-media>
|
||||
|
||||
<md-card-header>
|
||||
<h2 class="md-subheading">{{item.title}}</h2>
|
||||
<div class="md-subhead">
|
||||
<md-icon>access_time</md-icon>
|
||||
<span>{{formatDateTime(item.create_time)}}</span>
|
||||
</div>
|
||||
<div class="md-subhead">
|
||||
<md-icon>location_on</md-icon>
|
||||
<span>{{item.total_addr}}</span>
|
||||
</div>
|
||||
</md-card-header>
|
||||
</md-card-area>
|
||||
</md-card>
|
||||
</md-content>
|
||||
</template>
|
||||
</template>
|
||||
<md-speed-dial class="md-bottom-right" style="margin-bottom: 56px; z-index:1000;">
|
||||
<md-speed-dial-target class="md-primary" @click="getLostAndFount(true)">
|
||||
<md-icon>cached</md-icon>
|
||||
</md-speed-dial-target>
|
||||
</md-speed-dial>
|
||||
<md-snackbar
|
||||
md-position="center"
|
||||
:md-active.sync="show_snackbar"
|
||||
:md-duration="1500"
|
||||
md-persistent
|
||||
>
|
||||
<span>{{ snakebar_msg }}</span>
|
||||
</md-snackbar>
|
||||
<div :style="`height:56px`"></div>
|
||||
</v-touch>
|
||||
</md-app-content>
|
||||
</md-app>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// @ is an alias to /src
|
||||
import { mapState, mapActions } from "vuex";
|
||||
import { setHtmlFontSize } from "@/utils/px2rem.js";
|
||||
import { api } from "@/axios/fetch.js";
|
||||
import { formatDateTime } from "@/utils/formatTime.js";
|
||||
|
||||
export default {
|
||||
name: "Home",
|
||||
data() {
|
||||
return {
|
||||
// 显示高度,控制md-app的最小高度,防止抽屉高度塌陷
|
||||
clientHeight: "",
|
||||
// snackbar控制
|
||||
show_snackbar: false,
|
||||
snakebar_msg: "",
|
||||
// 加载控制
|
||||
loading_switch: false,
|
||||
// 搜索框当前检索列表
|
||||
current_title: [],
|
||||
// 搜索输入内容
|
||||
search_content: "",
|
||||
// 搜索开始
|
||||
search_start: false,
|
||||
// 显示的密码列表
|
||||
show_list: [],
|
||||
// 页面类型
|
||||
page_type: "lost",
|
||||
// 失物标题
|
||||
lost_title: [],
|
||||
// 招领标题
|
||||
found_title: [],
|
||||
// 失物列表
|
||||
const_lost_list: [],
|
||||
// 招领列表
|
||||
const_found_list: []
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(["user_info", "settings", "laf"])
|
||||
},
|
||||
methods: {
|
||||
...mapActions(["setUserInfo", "setSettings", "setLaf", "setBarState"]),
|
||||
// 格式化时间
|
||||
formatDateTime,
|
||||
// 修改md-app的最小高度
|
||||
changeFixed(clientHeight) {
|
||||
//动态修改样式
|
||||
this.$refs.home.children[0].style.minHeight = clientHeight + "px";
|
||||
this.$refs.list_placeholder.parentNode.parentNode.style.maxHeight =
|
||||
clientHeight + "px";
|
||||
this.$refs.list_placeholder.parentNode.style.minHeight =
|
||||
clientHeight - 32 + "px";
|
||||
window.document.documentElement.setAttribute(
|
||||
"data-theme",
|
||||
this.settings.is_dark_mode ? "dark" : "light"
|
||||
);
|
||||
},
|
||||
|
||||
// 初始化
|
||||
init(from_created = true) {
|
||||
// 刷新vuex
|
||||
this.$store.replaceState(
|
||||
Object.assign(
|
||||
this.$store.state,
|
||||
JSON.parse(localStorage.getItem("storeState"))
|
||||
)
|
||||
);
|
||||
// 底部状态栏状态设置
|
||||
this.setBarState([true, this]);
|
||||
// 获取失物招领信true息
|
||||
if(from_created)
|
||||
this.getLostAndFount();
|
||||
},
|
||||
|
||||
// 获取失物招领信息
|
||||
getLostAndFount(force = false) {
|
||||
let now = new Date().getTime();
|
||||
if (!force && this.laf && now - this.laf.time < 1000 * 60) {
|
||||
console.log("当前信息未失效拦截");
|
||||
this.separateData(this.laf.list, true);
|
||||
return;
|
||||
}
|
||||
this.loading_switch = true;
|
||||
api
|
||||
.get("/get")
|
||||
.then(res => {
|
||||
let laf = {
|
||||
list: res.data.list,
|
||||
time: new Date().getTime()
|
||||
};
|
||||
this.setLaf([laf, this]);
|
||||
console.log("列表获取成功,缓存覆写成功");
|
||||
this.separateData(res.data.list);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
this.loading_switch = false;
|
||||
if (err.response && err.response.status != 500) {
|
||||
this.message(`${err.response.status}: ${err.response.data}`);
|
||||
} else {
|
||||
this.message("网络错误,请稍候重试");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 分离数据
|
||||
separateData(list, from_intercept = false) {
|
||||
// 失物列表
|
||||
let lost_list = [];
|
||||
// 招领列表
|
||||
let found_list = [];
|
||||
// 失物标题
|
||||
let lost_title = [];
|
||||
// 招领标题
|
||||
let found_title = [];
|
||||
// 遍历列表
|
||||
for (let item of list) {
|
||||
if (item.type == "lost") {
|
||||
lost_list.push(item);
|
||||
lost_title.push(item.title);
|
||||
} else {
|
||||
found_list.push(item);
|
||||
found_title.push(item.title);
|
||||
}
|
||||
}
|
||||
this.page_type = "lost";
|
||||
this.show_list = lost_list;
|
||||
this.lost_title = lost_title;
|
||||
this.found_title = found_title;
|
||||
this.const_lost_list = lost_list;
|
||||
this.const_found_list = found_list;
|
||||
this.loading_switch = false;
|
||||
console.log("数据分离完成");
|
||||
if (!from_intercept) this.message("刷新成功");
|
||||
},
|
||||
|
||||
// 调用snackBar
|
||||
message(msg) {
|
||||
this.snakebar_msg = msg;
|
||||
this.show_snackbar = true;
|
||||
},
|
||||
|
||||
// 切换搜索状态
|
||||
switchSearch() {
|
||||
// 没有内容拦截器
|
||||
if (this.show_list.length == 0) {
|
||||
console.log("点击搜索无内容拦截");
|
||||
this.message("没有内容呀~");
|
||||
return;
|
||||
}
|
||||
this.search_start = !this.search_start;
|
||||
},
|
||||
|
||||
// 模糊搜索
|
||||
fuzzySearch(list) {
|
||||
// 空内容拦截器
|
||||
let search_content = this.search_content.trim();
|
||||
if (!search_content) {
|
||||
this.show_list = list;
|
||||
return;
|
||||
}
|
||||
let search_res = [];
|
||||
for (let item of list) {
|
||||
if (this.generReg(search_content).test(item.title)) {
|
||||
search_res.push(item);
|
||||
}
|
||||
}
|
||||
this.show_list = search_res;
|
||||
console.log("模糊搜索完成");
|
||||
},
|
||||
|
||||
// 模糊搜索辅助轮
|
||||
generReg(val) {
|
||||
let head = "(.*)(";
|
||||
let tail = ")(.*)";
|
||||
let body = val.split("").join(")(.*)(");
|
||||
return new RegExp(head + body + tail, "i");
|
||||
},
|
||||
|
||||
// 跳转详情
|
||||
turnToDetail(id) {
|
||||
console.log(id);
|
||||
this.$router.push({ path: "/detail", query: { id: id } });
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
},
|
||||
mounted() {
|
||||
// 获取浏览器可视区域高度
|
||||
this.clientHeight = `${document.documentElement.clientHeight}`;
|
||||
//document.body.clientWidth;
|
||||
window.onresize = function temp() {
|
||||
this.clientHeight = `${document.documentElement.clientHeight}`;
|
||||
setHtmlFontSize();
|
||||
}.bind(this);
|
||||
this.$refs.list_placeholder.parentNode.parentNode.addEventListener(
|
||||
"scroll",
|
||||
this.scroll
|
||||
);
|
||||
},
|
||||
watch: {
|
||||
// 如果 `clientHeight` 发生改变,这个函数就会运行
|
||||
clientHeight: function() {
|
||||
this.changeFixed(this.clientHeight);
|
||||
},
|
||||
// 如果 `search_start` 发生改变,这个函数就会运行
|
||||
search_start: function() {
|
||||
if (this.search_start) {
|
||||
if (this.page_type == "lost") {
|
||||
this.current_title = JSON.parse(JSON.stringify(this.lost_title));
|
||||
} else {
|
||||
this.current_title = JSON.parse(JSON.stringify(this.found_title));
|
||||
}
|
||||
console.log("搜索阵列展开, 检索列表覆写完成");
|
||||
} else {
|
||||
this.search_content = "";
|
||||
if (this.page_type == "lost") {
|
||||
this.show_list = JSON.parse(JSON.stringify(this.const_lost_list));
|
||||
} else {
|
||||
this.show_list = JSON.parse(JSON.stringify(this.const_found_list));
|
||||
}
|
||||
console.log("搜索阵列关闭,显示列表覆写完成");
|
||||
}
|
||||
},
|
||||
// 如果 `search_content` 发生改变,这个函数就会运行
|
||||
search_content: function() {
|
||||
// 确保只有在搜索阵列展开后才会进行搜索
|
||||
if (this.search_start) {
|
||||
if (this.page_type == "lost") {
|
||||
this.fuzzySearch(JSON.parse(JSON.stringify(this.const_lost_list)));
|
||||
} else {
|
||||
this.fuzzySearch(JSON.parse(JSON.stringify(this.const_found_list)));
|
||||
}
|
||||
}
|
||||
},
|
||||
// 如果 `page_type` 发生改变,这个函数就会运行
|
||||
page_type: function() {
|
||||
if (this.page_type == "lost") {
|
||||
this.show_list = JSON.parse(JSON.stringify(this.const_lost_list));
|
||||
this.current_title = JSON.parse(JSON.stringify(this.lost_title));
|
||||
console.log(`显示列表覆写完成,当前页面性质:${this.page_type}`);
|
||||
} else {
|
||||
this.show_list = JSON.parse(JSON.stringify(this.const_found_list));
|
||||
this.current_title = JSON.parse(JSON.stringify(this.found_title));
|
||||
console.log(`显示列表覆写完成,当前页面性质:${this.page_type}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {},
|
||||
components: {},
|
||||
activated() {
|
||||
this.init(false)
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss" type="text/scss">
|
||||
@import "../../style/main";
|
||||
.home {
|
||||
width: 100%;
|
||||
// max-width: 500px;
|
||||
min-height: 100%;
|
||||
background: #fff;
|
||||
// margin: 0 auto;
|
||||
.drawer-banner {
|
||||
padding: 1rem 0.3rem 0.3rem 0.3rem;
|
||||
box-sizing: border-box;
|
||||
background: $main-color;
|
||||
color: #fff;
|
||||
position: relative;
|
||||
.default-avatar {
|
||||
color: #fff;
|
||||
margin-bottom: 0.3rem;
|
||||
}
|
||||
.md-caption {
|
||||
color: #fbfbfb;
|
||||
}
|
||||
}
|
||||
.md-toolbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
}
|
||||
.md-toolbar-row {
|
||||
order: 12;
|
||||
}
|
||||
.md-speed-dial.md-bottom-right {
|
||||
position: fixed !important;
|
||||
}
|
||||
}
|
||||
// Demo purposes only
|
||||
.md-drawer {
|
||||
width: 240px;
|
||||
max-width: calc(100vw - 125px);
|
||||
}
|
||||
.md-app-scroller > div {
|
||||
overflow-y: scroll !important;
|
||||
}
|
||||
.face {
|
||||
color: #fff !important;
|
||||
}
|
||||
.page-title {
|
||||
flex: 1;
|
||||
min-height: 56px;
|
||||
line-height: 56px;
|
||||
}
|
||||
.sroll-top-area {
|
||||
flex: 1;
|
||||
min-height: 56px;
|
||||
}
|
||||
.md-bottom-bar.md-type-fixed .md-bottom-bar-item {
|
||||
max-width: 1000px !important;
|
||||
}
|
||||
.md-app-content .md-card {
|
||||
margin: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.md-toolbar .md-toolbar-offset {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
.md-toolbar .md-tabs {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
.md-card-example {
|
||||
.md-subhead {
|
||||
.md-icon {
|
||||
$size: 16px;
|
||||
|
||||
width: $size;
|
||||
min-width: $size;
|
||||
height: $size;
|
||||
font-size: $size !important;
|
||||
}
|
||||
|
||||
span {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
18
vue.config.js
Normal file
@ -0,0 +1,18 @@
|
||||
module.exports = {
|
||||
productionSourceMap: false,
|
||||
// publicPath: process.env.NODE_ENV === 'production' ? '/verify/' : '/',
|
||||
pwa: {
|
||||
name: '曳光控制台',
|
||||
themeColor: '#448aff',
|
||||
workboxOptions: {
|
||||
skipWaiting: true
|
||||
},
|
||||
iconPaths: {
|
||||
favicon32: 'favicon.ico',
|
||||
favicon16: 'favicon.ico',
|
||||
appleTouchIcon: 'favicon.ico',
|
||||
maskIcon: 'favicon.ico',
|
||||
msTileImage: 'favicon.ico'
|
||||
}
|
||||
}
|
||||
}
|