@ -0,0 +1,25 @@ |
|||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. |
||||
|
|
||||
|
# dependencies |
||||
|
/node_modules |
||||
|
/.pnp |
||||
|
.pnp.js |
||||
|
|
||||
|
# testing |
||||
|
/coverage |
||||
|
|
||||
|
# production |
||||
|
/build |
||||
|
|
||||
|
# misc |
||||
|
.DS_Store |
||||
|
.env.local |
||||
|
.env.development.local |
||||
|
.env.test.local |
||||
|
.env.production.local |
||||
|
|
||||
|
npm-debug.log* |
||||
|
yarn-debug.log* |
||||
|
yarn-error.log* |
||||
|
|
||||
|
/.vscode |
||||
@ -0,0 +1,70 @@ |
|||||
|
# Getting Started with Create React App |
||||
|
|
||||
|
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). |
||||
|
|
||||
|
## Available Scripts |
||||
|
|
||||
|
In the project directory, you can run: |
||||
|
|
||||
|
### `yarn start` |
||||
|
|
||||
|
Runs the app in the development mode.\ |
||||
|
Open [http://localhost:3000](http://localhost:3000) to view it in the browser. |
||||
|
|
||||
|
The page will reload if you make edits.\ |
||||
|
You will also see any lint errors in the console. |
||||
|
|
||||
|
### `yarn test` |
||||
|
|
||||
|
Launches the test runner in the interactive watch mode.\ |
||||
|
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. |
||||
|
|
||||
|
### `yarn build` |
||||
|
|
||||
|
Builds the app for production to the `build` folder.\ |
||||
|
It correctly bundles React in production mode and optimizes the build for the best performance. |
||||
|
|
||||
|
The build is minified and the filenames include the hashes.\ |
||||
|
Your app is ready to be deployed! |
||||
|
|
||||
|
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. |
||||
|
|
||||
|
### `yarn eject` |
||||
|
|
||||
|
**Note: this is a one-way operation. Once you `eject`, you can’t go back!** |
||||
|
|
||||
|
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. |
||||
|
|
||||
|
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. |
||||
|
|
||||
|
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. |
||||
|
|
||||
|
## Learn More |
||||
|
|
||||
|
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). |
||||
|
|
||||
|
To learn React, check out the [React documentation](https://reactjs.org/). |
||||
|
|
||||
|
### Code Splitting |
||||
|
|
||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) |
||||
|
|
||||
|
### Analyzing the Bundle Size |
||||
|
|
||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) |
||||
|
|
||||
|
### Making a Progressive Web App |
||||
|
|
||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) |
||||
|
|
||||
|
### Advanced Configuration |
||||
|
|
||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) |
||||
|
|
||||
|
### Deployment |
||||
|
|
||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) |
||||
|
|
||||
|
### `yarn build` fails to minify |
||||
|
|
||||
|
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) |
||||
@ -0,0 +1,52 @@ |
|||||
|
{ |
||||
|
"name": "qmplaza", |
||||
|
"version": "0.1.0", |
||||
|
"private": true, |
||||
|
"homepage": "./", |
||||
|
"dependencies": { |
||||
|
"@apollo/client": "^3.3.11", |
||||
|
"@testing-library/jest-dom": "^5.11.4", |
||||
|
"@testing-library/react": "^11.1.0", |
||||
|
"@testing-library/user-event": "^12.1.10", |
||||
|
"antd-mobile": "^2.3.4", |
||||
|
"apollo-cache-inmemory": "^1.6.6", |
||||
|
"apollo-client": "^2.6.10", |
||||
|
"apollo-link-http": "^1.5.17", |
||||
|
"axios": "^0.21.1", |
||||
|
"graphql": "^15.5.0", |
||||
|
"node-sass": "^5.0.0", |
||||
|
"react": "^17.0.1", |
||||
|
"react-dom": "^17.0.1", |
||||
|
"react-infinite-scroller": "^1.2.4", |
||||
|
"react-lazy-load-image-component": "^1.5.1", |
||||
|
"react-modal": "^3.12.1", |
||||
|
"react-router-dom": "^5.2.0", |
||||
|
"react-scripts": "4.0.3", |
||||
|
"vconsole": "^3.4.0", |
||||
|
"web-vitals": "^1.0.1" |
||||
|
}, |
||||
|
"scripts": { |
||||
|
"start": "react-scripts start", |
||||
|
"build": "react-scripts build && coscmd config -a AKIDjS6at7fjTAVgggDNCrogRiZTSL304DPR -s FPPGCXr4xgZRLKDC8tZGp7HTZXlqx0gU -b lg-cjdqwkbo-1256266248 -r ap-shanghai && coscmd delete qmplaza -f && coscmd upload -r build qmplaza", |
||||
|
"test": "react-scripts test", |
||||
|
"eject": "react-scripts eject" |
||||
|
}, |
||||
|
"eslintConfig": { |
||||
|
"extends": [ |
||||
|
"react-app", |
||||
|
"react-app/jest" |
||||
|
] |
||||
|
}, |
||||
|
"browserslist": { |
||||
|
"production": [ |
||||
|
">0.2%", |
||||
|
"not dead", |
||||
|
"not op_mini all" |
||||
|
], |
||||
|
"development": [ |
||||
|
"last 1 chrome version", |
||||
|
"last 1 firefox version", |
||||
|
"last 1 safari version" |
||||
|
] |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,650 @@ |
|||||
|
! function (t, n) { |
||||
|
"object" == typeof exports ? module.exports = exports = n() : "function" == typeof define && define.amd ? define([], n) : t.CryptoJS = n() |
||||
|
}(this, function () { |
||||
|
var t = t || function (t, n) { |
||||
|
var i = Object.create || function () { |
||||
|
function t() {} |
||||
|
return function (n) { |
||||
|
var i; |
||||
|
return t.prototype = n, i = new t, t.prototype = null, i |
||||
|
} |
||||
|
}(), |
||||
|
e = {}, |
||||
|
r = e.lib = {}, |
||||
|
o = r.Base = function () { |
||||
|
return { |
||||
|
extend: function (t) { |
||||
|
var n = i(this); |
||||
|
return t && n.mixIn(t), n.hasOwnProperty("init") && this.init !== n.init || (n.init = function () { |
||||
|
n.$super.init.apply(this, arguments) |
||||
|
}), n.init.prototype = n, n.$super = this, n |
||||
|
}, |
||||
|
create: function () { |
||||
|
var t = this.extend(); |
||||
|
return t.init.apply(t, arguments), t |
||||
|
}, |
||||
|
init: function () {}, |
||||
|
mixIn: function (t) { |
||||
|
for (var n in t) t.hasOwnProperty(n) && (this[n] = t[n]); |
||||
|
t.hasOwnProperty("toString") && (this.toString = t.toString) |
||||
|
}, |
||||
|
clone: function () { |
||||
|
return this.init.prototype.extend(this) |
||||
|
} |
||||
|
} |
||||
|
}(), |
||||
|
s = r.WordArray = o.extend({ |
||||
|
init: function (t, i) { |
||||
|
t = this.words = t || [], i != n ? this.sigBytes = i : this.sigBytes = 4 * t.length |
||||
|
}, |
||||
|
toString: function (t) { |
||||
|
return (t || c).stringify(this) |
||||
|
}, |
||||
|
concat: function (t) { |
||||
|
var n = this.words, |
||||
|
i = t.words, |
||||
|
e = this.sigBytes, |
||||
|
r = t.sigBytes; |
||||
|
if (this.clamp(), e % 4) |
||||
|
for (var o = 0; o < r; o++) { |
||||
|
var s = i[o >>> 2] >>> 24 - o % 4 * 8 & 255; |
||||
|
n[e + o >>> 2] |= s << 24 - (e + o) % 4 * 8 |
||||
|
} else |
||||
|
for (var o = 0; o < r; o += 4) n[e + o >>> 2] = i[o >>> 2]; |
||||
|
return this.sigBytes += r, this |
||||
|
}, |
||||
|
clamp: function () { |
||||
|
var n = this.words, |
||||
|
i = this.sigBytes; |
||||
|
n[i >>> 2] &= 4294967295 << 32 - i % 4 * 8, n.length = t.ceil(i / 4) |
||||
|
}, |
||||
|
clone: function () { |
||||
|
var t = o.clone.call(this); |
||||
|
return t.words = this.words.slice(0), t |
||||
|
}, |
||||
|
random: function (n) { |
||||
|
for (var i, e = [], r = function (n) { |
||||
|
var n = n, |
||||
|
i = 987654321, |
||||
|
e = 4294967295; |
||||
|
return function () { |
||||
|
i = 36969 * (65535 & i) + (i >> 16) & e, n = 18e3 * (65535 & n) + (n >> 16) & e; |
||||
|
var r = (i << 16) + n & e; |
||||
|
return r /= 4294967296, r += .5, r * (t.random() > .5 ? 1 : -1) |
||||
|
} |
||||
|
}, o = 0; o < n; o += 4) { |
||||
|
var a = r(4294967296 * (i || t.random())); |
||||
|
i = 987654071 * a(), e.push(4294967296 * a() | 0) |
||||
|
} |
||||
|
return new s.init(e, n) |
||||
|
} |
||||
|
}), |
||||
|
a = e.enc = {}, |
||||
|
c = a.Hex = { |
||||
|
stringify: function (t) { |
||||
|
for (var n = t.words, i = t.sigBytes, e = [], r = 0; r < i; r++) { |
||||
|
var o = n[r >>> 2] >>> 24 - r % 4 * 8 & 255; |
||||
|
e.push((o >>> 4).toString(16)), e.push((15 & o).toString(16)) |
||||
|
} |
||||
|
return e.join("") |
||||
|
}, |
||||
|
parse: function (t) { |
||||
|
for (var n = t.length, i = [], e = 0; e < n; e += 2) i[e >>> 3] |= parseInt(t.substr(e, 2), 16) << 24 - e % 8 * 4; |
||||
|
return new s.init(i, n / 2) |
||||
|
} |
||||
|
}, |
||||
|
u = a.Latin1 = { |
||||
|
stringify: function (t) { |
||||
|
for (var n = t.words, i = t.sigBytes, e = [], r = 0; r < i; r++) { |
||||
|
var o = n[r >>> 2] >>> 24 - r % 4 * 8 & 255; |
||||
|
e.push(String.fromCharCode(o)) |
||||
|
} |
||||
|
return e.join("") |
||||
|
}, |
||||
|
parse: function (t) { |
||||
|
for (var n = t.length, i = [], e = 0; e < n; e++) i[e >>> 2] |= (255 & t.charCodeAt(e)) << 24 - e % 4 * 8; |
||||
|
return new s.init(i, n) |
||||
|
} |
||||
|
}, |
||||
|
f = a.Utf8 = { |
||||
|
stringify: function (t) { |
||||
|
try { |
||||
|
return decodeURIComponent(escape(u.stringify(t))) |
||||
|
} catch (t) { |
||||
|
throw new Error("Malformed UTF-8 data") |
||||
|
} |
||||
|
}, |
||||
|
parse: function (t) { |
||||
|
return u.parse(unescape(encodeURIComponent(t))) |
||||
|
} |
||||
|
}, |
||||
|
h = r.BufferedBlockAlgorithm = o.extend({ |
||||
|
reset: function () { |
||||
|
this._data = new s.init, this._nDataBytes = 0 |
||||
|
}, |
||||
|
_append: function (t) { |
||||
|
"string" == typeof t && (t = f.parse(t)), this._data.concat(t), this._nDataBytes += t.sigBytes |
||||
|
}, |
||||
|
_process: function (n) { |
||||
|
var i = this._data, |
||||
|
e = i.words, |
||||
|
r = i.sigBytes, |
||||
|
o = this.blockSize, |
||||
|
a = 4 * o, |
||||
|
c = r / a; |
||||
|
c = n ? t.ceil(c) : t.max((0 | c) - this._minBufferSize, 0); |
||||
|
var u = c * o, |
||||
|
f = t.min(4 * u, r); |
||||
|
if (u) { |
||||
|
for (var h = 0; h < u; h += o) this._doProcessBlock(e, h); |
||||
|
var p = e.splice(0, u); |
||||
|
i.sigBytes -= f |
||||
|
} |
||||
|
return new s.init(p, f) |
||||
|
}, |
||||
|
clone: function () { |
||||
|
var t = o.clone.call(this); |
||||
|
return t._data = this._data.clone(), t |
||||
|
}, |
||||
|
_minBufferSize: 0 |
||||
|
}), |
||||
|
p = (r.Hasher = h.extend({ |
||||
|
cfg: o.extend(), |
||||
|
init: function (t) { |
||||
|
this.cfg = this.cfg.extend(t), this.reset() |
||||
|
}, |
||||
|
reset: function () { |
||||
|
h.reset.call(this), this._doReset() |
||||
|
}, |
||||
|
update: function (t) { |
||||
|
return this._append(t), this._process(), this |
||||
|
}, |
||||
|
finalize: function (t) { |
||||
|
t && this._append(t); |
||||
|
var n = this._doFinalize(); |
||||
|
return n |
||||
|
}, |
||||
|
blockSize: 16, |
||||
|
_createHelper: function (t) { |
||||
|
return function (n, i) { |
||||
|
return new t.init(i).finalize(n) |
||||
|
} |
||||
|
}, |
||||
|
_createHmacHelper: function (t) { |
||||
|
return function (n, i) { |
||||
|
return new p.HMAC.init(t, i).finalize(n) |
||||
|
} |
||||
|
} |
||||
|
}), e.algo = {}); |
||||
|
return e |
||||
|
}(Math); |
||||
|
return t |
||||
|
}); |
||||
|
//# sourceMappingURL=core.min.js.map
|
||||
|
! function (e, t, i) { |
||||
|
"object" == typeof exports ? module.exports = exports = t(require("./core.min"), require("./sha1.min"), require("./hmac.min")) : "function" == typeof define && define.amd ? define(["./core.min", "./sha1.min", "./hmac.min"], t) : t(e.CryptoJS) |
||||
|
}(this, function (e) { |
||||
|
return function () { |
||||
|
var t = e, |
||||
|
i = t.lib, |
||||
|
r = i.Base, |
||||
|
n = i.WordArray, |
||||
|
o = t.algo, |
||||
|
a = o.MD5, |
||||
|
c = o.EvpKDF = r.extend({ |
||||
|
cfg: r.extend({ |
||||
|
keySize: 4, |
||||
|
hasher: a, |
||||
|
iterations: 1 |
||||
|
}), |
||||
|
init: function (e) { |
||||
|
this.cfg = this.cfg.extend(e) |
||||
|
}, |
||||
|
compute: function (e, t) { |
||||
|
for (var i = this.cfg, r = i.hasher.create(), o = n.create(), a = o.words, c = i.keySize, f = i.iterations; a.length < c;) { |
||||
|
s && r.update(s); |
||||
|
var s = r.update(e).finalize(t); |
||||
|
r.reset(); |
||||
|
for (var u = 1; u < f; u++) s = r.finalize(s), r.reset(); |
||||
|
o.concat(s) |
||||
|
} |
||||
|
return o.sigBytes = 4 * c, o |
||||
|
} |
||||
|
}); |
||||
|
t.EvpKDF = function (e, t, i) { |
||||
|
return c.create(i).compute(e, t) |
||||
|
} |
||||
|
}(), e.EvpKDF |
||||
|
}); |
||||
|
//# sourceMappingURL=evpkdf.min.js.map
|
||||
|
! function (r, e) { |
||||
|
"object" == typeof exports ? module.exports = exports = e(require("./core.min")) : "function" == typeof define && define.amd ? define(["./core.min"], e) : e(r.CryptoJS) |
||||
|
}(this, function (r) { |
||||
|
return function () { |
||||
|
function e(r, e, t) { |
||||
|
for (var n = [], i = 0, o = 0; o < e; o++) |
||||
|
if (o % 4) { |
||||
|
var f = t[r.charCodeAt(o - 1)] << o % 4 * 2, |
||||
|
c = t[r.charCodeAt(o)] >>> 6 - o % 4 * 2; |
||||
|
n[i >>> 2] |= (f | c) << 24 - i % 4 * 8, i++ |
||||
|
} return a.create(n, i) |
||||
|
} |
||||
|
var t = r, |
||||
|
n = t.lib, |
||||
|
a = n.WordArray, |
||||
|
i = t.enc; |
||||
|
i.Base64 = { |
||||
|
stringify: function (r) { |
||||
|
var e = r.words, |
||||
|
t = r.sigBytes, |
||||
|
n = this._map; |
||||
|
r.clamp(); |
||||
|
for (var a = [], i = 0; i < t; i += 3) |
||||
|
for (var o = e[i >>> 2] >>> 24 - i % 4 * 8 & 255, f = e[i + 1 >>> 2] >>> 24 - (i + 1) % 4 * 8 & 255, c = e[i + 2 >>> 2] >>> 24 - (i + 2) % 4 * 8 & 255, s = o << 16 | f << 8 | c, h = 0; h < 4 && i + .75 * h < t; h++) a.push(n.charAt(s >>> 6 * (3 - h) & 63)); |
||||
|
var p = n.charAt(64); |
||||
|
if (p) |
||||
|
for (; a.length % 4;) a.push(p); |
||||
|
return a.join("") |
||||
|
}, |
||||
|
parse: function (r) { |
||||
|
var t = r.length, |
||||
|
n = this._map, |
||||
|
a = this._reverseMap; |
||||
|
if (!a) { |
||||
|
a = this._reverseMap = []; |
||||
|
for (var i = 0; i < n.length; i++) a[n.charCodeAt(i)] = i |
||||
|
} |
||||
|
var o = n.charAt(64); |
||||
|
if (o) { |
||||
|
var f = r.indexOf(o); |
||||
|
f !== -1 && (t = f) |
||||
|
} |
||||
|
return e(r, t, a) |
||||
|
}, |
||||
|
_map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" |
||||
|
} |
||||
|
}(), r.enc.Base64 |
||||
|
}); |
||||
|
//# sourceMappingURL=enc-base64.min.js.map
|
||||
|
! function (e, t, r) { |
||||
|
"object" == typeof exports ? module.exports = exports = t(require("./core.min"), require("./evpkdf.min")) : "function" == typeof define && define.amd ? define(["./core.min", "./evpkdf.min"], t) : t(e.CryptoJS) |
||||
|
}(this, function (e) { |
||||
|
e.lib.Cipher || function (t) { |
||||
|
var r = e, |
||||
|
i = r.lib, |
||||
|
n = i.Base, |
||||
|
c = i.WordArray, |
||||
|
o = i.BufferedBlockAlgorithm, |
||||
|
s = r.enc, |
||||
|
a = (s.Utf8, s.Base64), |
||||
|
f = r.algo, |
||||
|
p = f.EvpKDF, |
||||
|
d = i.Cipher = o.extend({ |
||||
|
cfg: n.extend(), |
||||
|
createEncryptor: function (e, t) { |
||||
|
return this.create(this._ENC_XFORM_MODE, e, t) |
||||
|
}, |
||||
|
createDecryptor: function (e, t) { |
||||
|
return this.create(this._DEC_XFORM_MODE, e, t) |
||||
|
}, |
||||
|
init: function (e, t, r) { |
||||
|
this.cfg = this.cfg.extend(r), this._xformMode = e, this._key = t, this.reset() |
||||
|
}, |
||||
|
reset: function () { |
||||
|
o.reset.call(this), this._doReset() |
||||
|
}, |
||||
|
process: function (e) { |
||||
|
return this._append(e), this._process() |
||||
|
}, |
||||
|
finalize: function (e) { |
||||
|
e && this._append(e); |
||||
|
var t = this._doFinalize(); |
||||
|
return t |
||||
|
}, |
||||
|
keySize: 4, |
||||
|
ivSize: 4, |
||||
|
_ENC_XFORM_MODE: 1, |
||||
|
_DEC_XFORM_MODE: 2, |
||||
|
_createHelper: function () { |
||||
|
function e(e) { |
||||
|
return "string" == typeof e ? B : x |
||||
|
} |
||||
|
return function (t) { |
||||
|
return { |
||||
|
encrypt: function (r, i, n) { |
||||
|
return e(i).encrypt(t, r, i, n) |
||||
|
}, |
||||
|
decrypt: function (r, i, n) { |
||||
|
return e(i).decrypt(t, r, i, n) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}() |
||||
|
}), |
||||
|
h = (i.StreamCipher = d.extend({ |
||||
|
_doFinalize: function () { |
||||
|
var e = this._process(!0); |
||||
|
return e |
||||
|
}, |
||||
|
blockSize: 1 |
||||
|
}), r.mode = {}), |
||||
|
u = i.BlockCipherMode = n.extend({ |
||||
|
createEncryptor: function (e, t) { |
||||
|
return this.Encryptor.create(e, t) |
||||
|
}, |
||||
|
createDecryptor: function (e, t) { |
||||
|
return this.Decryptor.create(e, t) |
||||
|
}, |
||||
|
init: function (e, t) { |
||||
|
this._cipher = e, this._iv = t |
||||
|
} |
||||
|
}), |
||||
|
l = h.CBC = function () { |
||||
|
function e(e, r, i) { |
||||
|
var n = this._iv; |
||||
|
if (n) { |
||||
|
var c = n; |
||||
|
this._iv = t |
||||
|
} else var c = this._prevBlock; |
||||
|
for (var o = 0; o < i; o++) e[r + o] ^= c[o] |
||||
|
} |
||||
|
var r = u.extend(); |
||||
|
return r.Encryptor = r.extend({ |
||||
|
processBlock: function (t, r) { |
||||
|
var i = this._cipher, |
||||
|
n = i.blockSize; |
||||
|
e.call(this, t, r, n), i.encryptBlock(t, r), this._prevBlock = t.slice(r, r + n) |
||||
|
} |
||||
|
}), r.Decryptor = r.extend({ |
||||
|
processBlock: function (t, r) { |
||||
|
var i = this._cipher, |
||||
|
n = i.blockSize, |
||||
|
c = t.slice(r, r + n); |
||||
|
i.decryptBlock(t, r), e.call(this, t, r, n), this._prevBlock = c |
||||
|
} |
||||
|
}), r |
||||
|
}(), |
||||
|
_ = r.pad = {}, |
||||
|
v = _.Pkcs7 = { |
||||
|
pad: function (e, t) { |
||||
|
for (var r = 4 * t, i = r - e.sigBytes % r, n = i << 24 | i << 16 | i << 8 | i, o = [], s = 0; s < i; s += 4) o.push(n); |
||||
|
var a = c.create(o, i); |
||||
|
e.concat(a) |
||||
|
}, |
||||
|
unpad: function (e) { |
||||
|
var t = 255 & e.words[e.sigBytes - 1 >>> 2]; |
||||
|
e.sigBytes -= t |
||||
|
} |
||||
|
}, |
||||
|
y = (i.BlockCipher = d.extend({ |
||||
|
cfg: d.cfg.extend({ |
||||
|
mode: l, |
||||
|
padding: v |
||||
|
}), |
||||
|
reset: function () { |
||||
|
d.reset.call(this); |
||||
|
var e = this.cfg, |
||||
|
t = e.iv, |
||||
|
r = e.mode; |
||||
|
if (this._xformMode == this._ENC_XFORM_MODE) var i = r.createEncryptor; |
||||
|
else { |
||||
|
var i = r.createDecryptor; |
||||
|
this._minBufferSize = 1 |
||||
|
} |
||||
|
this._mode && this._mode.__creator == i ? this._mode.init(this, t && t.words) : (this._mode = i.call(r, this, t && t.words), this._mode.__creator = i) |
||||
|
}, |
||||
|
_doProcessBlock: function (e, t) { |
||||
|
this._mode.processBlock(e, t) |
||||
|
}, |
||||
|
_doFinalize: function () { |
||||
|
var e = this.cfg.padding; |
||||
|
if (this._xformMode == this._ENC_XFORM_MODE) { |
||||
|
e.pad(this._data, this.blockSize); |
||||
|
var t = this._process(!0) |
||||
|
} else { |
||||
|
var t = this._process(!0); |
||||
|
e.unpad(t) |
||||
|
} |
||||
|
return t |
||||
|
}, |
||||
|
blockSize: 4 |
||||
|
}), i.CipherParams = n.extend({ |
||||
|
init: function (e) { |
||||
|
this.mixIn(e) |
||||
|
}, |
||||
|
toString: function (e) { |
||||
|
return (e || this.formatter).stringify(this) |
||||
|
} |
||||
|
})), |
||||
|
m = r.format = {}, |
||||
|
k = m.OpenSSL = { |
||||
|
stringify: function (e) { |
||||
|
var t = e.ciphertext, |
||||
|
r = e.salt; |
||||
|
if (r) var i = c.create([1398893684, 1701076831]).concat(r).concat(t); |
||||
|
else var i = t; |
||||
|
return i.toString(a) |
||||
|
}, |
||||
|
parse: function (e) { |
||||
|
var t = a.parse(e), |
||||
|
r = t.words; |
||||
|
if (1398893684 == r[0] && 1701076831 == r[1]) { |
||||
|
var i = c.create(r.slice(2, 4)); |
||||
|
r.splice(0, 4), t.sigBytes -= 16 |
||||
|
} |
||||
|
return y.create({ |
||||
|
ciphertext: t, |
||||
|
salt: i |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
x = i.SerializableCipher = n.extend({ |
||||
|
cfg: n.extend({ |
||||
|
format: k |
||||
|
}), |
||||
|
encrypt: function (e, t, r, i) { |
||||
|
i = this.cfg.extend(i); |
||||
|
var n = e.createEncryptor(r, i), |
||||
|
c = n.finalize(t), |
||||
|
o = n.cfg; |
||||
|
return y.create({ |
||||
|
ciphertext: c, |
||||
|
key: r, |
||||
|
iv: o.iv, |
||||
|
algorithm: e, |
||||
|
mode: o.mode, |
||||
|
padding: o.padding, |
||||
|
blockSize: e.blockSize, |
||||
|
formatter: i.format |
||||
|
}) |
||||
|
}, |
||||
|
decrypt: function (e, t, r, i) { |
||||
|
i = this.cfg.extend(i), t = this._parse(t, i.format); |
||||
|
var n = e.createDecryptor(r, i).finalize(t.ciphertext); |
||||
|
return n |
||||
|
}, |
||||
|
_parse: function (e, t) { |
||||
|
return "string" == typeof e ? t.parse(e, this) : e |
||||
|
} |
||||
|
}), |
||||
|
g = r.kdf = {}, |
||||
|
S = g.OpenSSL = { |
||||
|
execute: function (e, t, r, i) { |
||||
|
i || (i = c.random(8)); |
||||
|
var n = p.create({ |
||||
|
keySize: t + r |
||||
|
}).compute(e, i), |
||||
|
o = c.create(n.words.slice(t), 4 * r); |
||||
|
return n.sigBytes = 4 * t, y.create({ |
||||
|
key: n, |
||||
|
iv: o, |
||||
|
salt: i |
||||
|
}) |
||||
|
} |
||||
|
}, |
||||
|
B = i.PasswordBasedCipher = x.extend({ |
||||
|
cfg: x.cfg.extend({ |
||||
|
kdf: S |
||||
|
}), |
||||
|
encrypt: function (e, t, r, i) { |
||||
|
i = this.cfg.extend(i); |
||||
|
var n = i.kdf.execute(r, e.keySize, e.ivSize); |
||||
|
i.iv = n.iv; |
||||
|
var c = x.encrypt.call(this, e, t, n.key, i); |
||||
|
return c.mixIn(n), c |
||||
|
}, |
||||
|
decrypt: function (e, t, r, i) { |
||||
|
i = this.cfg.extend(i), t = this._parse(t, i.format); |
||||
|
var n = i.kdf.execute(r, e.keySize, e.ivSize, t.salt); |
||||
|
i.iv = n.iv; |
||||
|
var c = x.decrypt.call(this, e, t, n.key, i); |
||||
|
return c |
||||
|
} |
||||
|
}) |
||||
|
}() |
||||
|
}); |
||||
|
//# sourceMappingURL=cipher-core.min.js.map
|
||||
|
! function (e, i) { |
||||
|
"object" == typeof exports ? module.exports = exports = i(require("./core.min")) : "function" == typeof define && define.amd ? define(["./core.min"], i) : i(e.CryptoJS) |
||||
|
}(this, function (e) { |
||||
|
! function () { |
||||
|
var i = e, |
||||
|
t = i.lib, |
||||
|
n = t.Base, |
||||
|
s = i.enc, |
||||
|
r = s.Utf8, |
||||
|
o = i.algo; |
||||
|
o.HMAC = n.extend({ |
||||
|
init: function (e, i) { |
||||
|
e = this._hasher = new e.init, "string" == typeof i && (i = r.parse(i)); |
||||
|
var t = e.blockSize, |
||||
|
n = 4 * t; |
||||
|
i.sigBytes > n && (i = e.finalize(i)), i.clamp(); |
||||
|
for (var s = this._oKey = i.clone(), o = this._iKey = i.clone(), a = s.words, f = o.words, c = 0; c < t; c++) a[c] ^= 1549556828, f[c] ^= 909522486; |
||||
|
s.sigBytes = o.sigBytes = n, this.reset() |
||||
|
}, |
||||
|
reset: function () { |
||||
|
var e = this._hasher; |
||||
|
e.reset(), e.update(this._iKey) |
||||
|
}, |
||||
|
update: function (e) { |
||||
|
return this._hasher.update(e), this |
||||
|
}, |
||||
|
finalize: function (e) { |
||||
|
var i = this._hasher, |
||||
|
t = i.finalize(e); |
||||
|
i.reset(); |
||||
|
var n = i.finalize(this._oKey.clone().concat(t)); |
||||
|
return n |
||||
|
} |
||||
|
}) |
||||
|
}() |
||||
|
}); |
||||
|
//# sourceMappingURL=hmac.min.js.map
|
||||
|
! function (e, o, r) { |
||||
|
"object" == typeof exports ? module.exports = exports = o(require("./core.min"), require("./cipher-core.min")) : "function" == typeof define && define.amd ? define(["./core.min", "./cipher-core.min"], o) : o(e.CryptoJS) |
||||
|
}(this, function (e) { |
||||
|
return e.mode.ECB = function () { |
||||
|
var o = e.lib.BlockCipherMode.extend(); |
||||
|
return o.Encryptor = o.extend({ |
||||
|
processBlock: function (e, o) { |
||||
|
this._cipher.encryptBlock(e, o) |
||||
|
} |
||||
|
}), o.Decryptor = o.extend({ |
||||
|
processBlock: function (e, o) { |
||||
|
this._cipher.decryptBlock(e, o) |
||||
|
} |
||||
|
}), o |
||||
|
}(), e.mode.ECB |
||||
|
}); |
||||
|
//# sourceMappingURL=mode-ecb.min.js.map
|
||||
|
! function (e, r, i) { |
||||
|
"object" == typeof exports ? module.exports = exports = r(require("./core.min"), require("./cipher-core.min")) : "function" == typeof define && define.amd ? define(["./core.min", "./cipher-core.min"], r) : r(e.CryptoJS) |
||||
|
}(this, function (e) { |
||||
|
return e.pad.Pkcs7 |
||||
|
}); |
||||
|
//# sourceMappingURL=pad-pkcs7.min.js.map
|
||||
|
! function (e, r, i) { |
||||
|
"object" == typeof exports ? module.exports = exports = r(require("./core.min"), require("./enc-base64.min"), require("./md5.min"), require("./evpkdf.min"), require("./cipher-core.min")) : "function" == typeof define && define.amd ? define(["./core.min", "./enc-base64.min", "./md5.min", "./evpkdf.min", "./cipher-core.min"], r) : r(e.CryptoJS) |
||||
|
}(this, function (e) { |
||||
|
return function () { |
||||
|
var r = e, |
||||
|
i = r.lib, |
||||
|
n = i.BlockCipher, |
||||
|
o = r.algo, |
||||
|
t = [], |
||||
|
c = [], |
||||
|
s = [], |
||||
|
f = [], |
||||
|
a = [], |
||||
|
d = [], |
||||
|
u = [], |
||||
|
v = [], |
||||
|
h = [], |
||||
|
y = []; |
||||
|
! function () { |
||||
|
for (var e = [], r = 0; r < 256; r++) r < 128 ? e[r] = r << 1 : e[r] = r << 1 ^ 283; |
||||
|
for (var i = 0, n = 0, r = 0; r < 256; r++) { |
||||
|
var o = n ^ n << 1 ^ n << 2 ^ n << 3 ^ n << 4; |
||||
|
o = o >>> 8 ^ 255 & o ^ 99, t[i] = o, c[o] = i; |
||||
|
var p = e[i], |
||||
|
l = e[p], |
||||
|
_ = e[l], |
||||
|
k = 257 * e[o] ^ 16843008 * o; |
||||
|
s[i] = k << 24 | k >>> 8, f[i] = k << 16 | k >>> 16, a[i] = k << 8 | k >>> 24, d[i] = k; |
||||
|
var k = 16843009 * _ ^ 65537 * l ^ 257 * p ^ 16843008 * i; |
||||
|
u[o] = k << 24 | k >>> 8, v[o] = k << 16 | k >>> 16, h[o] = k << 8 | k >>> 24, y[o] = k, i ? (i = p ^ e[e[e[_ ^ p]]], n ^= e[e[n]]) : i = n = 1 |
||||
|
} |
||||
|
}(); |
||||
|
var p = [0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54], |
||||
|
l = o.AES = n.extend({ |
||||
|
_doReset: function () { |
||||
|
if (!this._nRounds || this._keyPriorReset !== this._key) { |
||||
|
for (var e = this._keyPriorReset = this._key, r = e.words, i = e.sigBytes / 4, n = this._nRounds = i + 6, o = 4 * (n + 1), c = this._keySchedule = [], s = 0; s < o; s++) |
||||
|
if (s < i) c[s] = r[s]; |
||||
|
else { |
||||
|
var f = c[s - 1]; |
||||
|
s % i ? i > 6 && s % i == 4 && (f = t[f >>> 24] << 24 | t[f >>> 16 & 255] << 16 | t[f >>> 8 & 255] << 8 | t[255 & f]) : (f = f << 8 | f >>> 24, f = t[f >>> 24] << 24 | t[f >>> 16 & 255] << 16 | t[f >>> 8 & 255] << 8 | t[255 & f], f ^= p[s / i | 0] << 24), c[s] = c[s - i] ^ f |
||||
|
} for (var a = this._invKeySchedule = [], d = 0; d < o; d++) { |
||||
|
var s = o - d; |
||||
|
if (d % 4) var f = c[s]; |
||||
|
else var f = c[s - 4]; |
||||
|
d < 4 || s <= 4 ? a[d] = f : a[d] = u[t[f >>> 24]] ^ v[t[f >>> 16 & 255]] ^ h[t[f >>> 8 & 255]] ^ y[t[255 & f]] |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
encryptBlock: function (e, r) { |
||||
|
this._doCryptBlock(e, r, this._keySchedule, s, f, a, d, t) |
||||
|
}, |
||||
|
decryptBlock: function (e, r) { |
||||
|
var i = e[r + 1]; |
||||
|
e[r + 1] = e[r + 3], e[r + 3] = i, this._doCryptBlock(e, r, this._invKeySchedule, u, v, h, y, c); |
||||
|
var i = e[r + 1]; |
||||
|
e[r + 1] = e[r + 3], e[r + 3] = i |
||||
|
}, |
||||
|
_doCryptBlock: function (e, r, i, n, o, t, c, s) { |
||||
|
for (var f = this._nRounds, a = e[r] ^ i[0], d = e[r + 1] ^ i[1], u = e[r + 2] ^ i[2], v = e[r + 3] ^ i[3], h = 4, y = 1; y < f; y++) { |
||||
|
var p = n[a >>> 24] ^ o[d >>> 16 & 255] ^ t[u >>> 8 & 255] ^ c[255 & v] ^ i[h++], |
||||
|
l = n[d >>> 24] ^ o[u >>> 16 & 255] ^ t[v >>> 8 & 255] ^ c[255 & a] ^ i[h++], |
||||
|
_ = n[u >>> 24] ^ o[v >>> 16 & 255] ^ t[a >>> 8 & 255] ^ c[255 & d] ^ i[h++], |
||||
|
k = n[v >>> 24] ^ o[a >>> 16 & 255] ^ t[d >>> 8 & 255] ^ c[255 & u] ^ i[h++]; |
||||
|
a = p, d = l, u = _, v = k |
||||
|
} |
||||
|
var p = (s[a >>> 24] << 24 | s[d >>> 16 & 255] << 16 | s[u >>> 8 & 255] << 8 | s[255 & v]) ^ i[h++], |
||||
|
l = (s[d >>> 24] << 24 | s[u >>> 16 & 255] << 16 | s[v >>> 8 & 255] << 8 | s[255 & a]) ^ i[h++], |
||||
|
_ = (s[u >>> 24] << 24 | s[v >>> 16 & 255] << 16 | s[a >>> 8 & 255] << 8 | s[255 & d]) ^ i[h++], |
||||
|
k = (s[v >>> 24] << 24 | s[a >>> 16 & 255] << 16 | s[d >>> 8 & 255] << 8 | s[255 & u]) ^ i[h++]; |
||||
|
e[r] = p, e[r + 1] = l, e[r + 2] = _, e[r + 3] = k |
||||
|
}, |
||||
|
keySize: 8 |
||||
|
}); |
||||
|
r.AES = n._createHelper(l) |
||||
|
}(), e.AES |
||||
|
}); |
||||
|
//# sourceMappingURL=aes.min.js.map
|
||||
|
! function (e, n) { |
||||
|
"object" == typeof exports ? module.exports = exports = n(require("./core.min")) : "function" == typeof define && define.amd ? define(["./core.min"], n) : n(e.CryptoJS) |
||||
|
}(this, function (e) { |
||||
|
return e.enc.Utf8 |
||||
|
}); |
||||
|
//# sourceMappingURL=enc-utf8.min.js.map
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@ -0,0 +1,39 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="zh"> |
||||
|
<head> |
||||
|
<meta charset="utf-8" /> |
||||
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> |
||||
|
<meta |
||||
|
name="viewport" |
||||
|
content="width=device-width,initial-scale=1,user-scalable=no" |
||||
|
/> |
||||
|
<meta name="theme-color" content="#000000" /> |
||||
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> |
||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> |
||||
|
<script src="%PUBLIC_URL%/polyfill.js"></script> |
||||
|
<script src="%PUBLIC_URL%/es7.js"></script> |
||||
|
<script src="%PUBLIC_URL%/aes.js"></script> |
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js" |
||||
|
></script> |
||||
|
<link |
||||
|
rel="stylesheet" |
||||
|
href="https://res.wx.qq.com/open/libs/weui/2.0.1/weui.min.css" |
||||
|
/> |
||||
|
<script |
||||
|
type="text/javascript" |
||||
|
src="https://res.wx.qq.com/t/wx_fed/cdn_libs/res/weui/1.2.3/weui.min.js" |
||||
|
></script> |
||||
|
</head> |
||||
|
<body> |
||||
|
<style> |
||||
|
#root { |
||||
|
position: fixed; |
||||
|
width: 100vw; |
||||
|
height: 100vh; |
||||
|
} |
||||
|
</style> |
||||
|
<div id="root"></div> |
||||
|
</body> |
||||
|
</html> |
||||
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
@ -0,0 +1,25 @@ |
|||||
|
{ |
||||
|
"short_name": "React App", |
||||
|
"name": "Create React App Sample", |
||||
|
"icons": [ |
||||
|
{ |
||||
|
"src": "favicon.ico", |
||||
|
"sizes": "64x64 32x32 24x24 16x16", |
||||
|
"type": "image/x-icon" |
||||
|
}, |
||||
|
{ |
||||
|
"src": "logo192.png", |
||||
|
"type": "image/png", |
||||
|
"sizes": "192x192" |
||||
|
}, |
||||
|
{ |
||||
|
"src": "logo512.png", |
||||
|
"type": "image/png", |
||||
|
"sizes": "512x512" |
||||
|
} |
||||
|
], |
||||
|
"start_url": ".", |
||||
|
"display": "standalone", |
||||
|
"theme_color": "#000000", |
||||
|
"background_color": "#ffffff" |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
# https://www.robotstxt.org/robotstxt.html |
||||
|
User-agent: * |
||||
|
Disallow: |
||||
@ -0,0 +1,14 @@ |
|||||
|
import { HashRouter, Switch, Route } from "react-router-dom"; |
||||
|
import Index from "./pages/Index/Index"; |
||||
|
import "./App.scss"; |
||||
|
function App() { |
||||
|
return ( |
||||
|
<HashRouter basename=""> |
||||
|
<Switch> |
||||
|
<Route path="/" component={Index} /> |
||||
|
</Switch> |
||||
|
</HashRouter> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
export default App; |
||||
@ -0,0 +1,17 @@ |
|||||
|
.toast { |
||||
|
.weui-icon_toast { |
||||
|
display: none; |
||||
|
} |
||||
|
.weui-toast { |
||||
|
display: inline-block; |
||||
|
width: auto; |
||||
|
min-height: auto; |
||||
|
margin: auto; |
||||
|
padding: 16px; |
||||
|
transform-origin: center; |
||||
|
transform: translateX(-50%); |
||||
|
.weui-toast__content { |
||||
|
margin: 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
import { render, screen } from '@testing-library/react'; |
||||
|
import App from './App'; |
||||
|
|
||||
|
test('renders learn react link', () => { |
||||
|
render(<App />); |
||||
|
const linkElement = screen.getByText(/learn react/i); |
||||
|
expect(linkElement).toBeInTheDocument(); |
||||
|
}); |
||||
@ -0,0 +1,211 @@ |
|||||
|
import "./style.scss"; |
||||
|
import React, { useState, useEffect } from "react"; |
||||
|
|
||||
|
import search from "./search.png"; |
||||
|
import close from "./close.png"; |
||||
|
import go from "./go.png"; |
||||
|
import arrow from "./arrow.png"; |
||||
|
import ShopsWithFilter from "../ShopsWithFilter/ShopsWithFilter"; |
||||
|
const STATES = { |
||||
|
init: 0, |
||||
|
moreFac: 1, |
||||
|
facList: 2, |
||||
|
}; |
||||
|
export const DefaultPopupStates = STATES; |
||||
|
const initMarginBottom = 47; |
||||
|
const DefaultPopup = ({ |
||||
|
state, |
||||
|
setState, |
||||
|
onSearch, |
||||
|
facilities, |
||||
|
onClickFac, |
||||
|
mall, |
||||
|
onClick, |
||||
|
setEnd, |
||||
|
hasTab, |
||||
|
blurMap = () => {}, |
||||
|
}) => { |
||||
|
const marginBottomStateMap = { |
||||
|
0: 47, |
||||
|
1: hasTab |
||||
|
? window.innerHeight - 52 - 179 - 98 |
||||
|
: window.innerHeight - 52 - 179, |
||||
|
}; |
||||
|
const baseMarginBottom = marginBottomStateMap[state]; |
||||
|
const [facList, setFacList] = useState(null); |
||||
|
const [focused, setFocused] = useState(null); |
||||
|
const [start, setStart] = useState(null); |
||||
|
const [marginBottom, setMarginBottom] = useState(null); |
||||
|
const [doTransition, setdoTransition] = useState(false); |
||||
|
useEffect(() => { |
||||
|
setdoTransition(true); |
||||
|
setMarginBottom(baseMarginBottom); |
||||
|
setTimeout(() => { |
||||
|
setdoTransition(false); |
||||
|
}, 500); |
||||
|
}, [state]); |
||||
|
const handleTouchStart = (e) => { |
||||
|
if (start) return; |
||||
|
setStart({ |
||||
|
identifier: e.changedTouches[0].identifier, |
||||
|
y: e.changedTouches[0].clientY, |
||||
|
}); |
||||
|
}; |
||||
|
const handleTouchMove = (e) => { |
||||
|
if (!start) return; |
||||
|
const touch = Array.from(e.changedTouches).find( |
||||
|
({ identifier }) => identifier === start.identifier |
||||
|
); |
||||
|
if (!touch) return; |
||||
|
const delta = touch.clientY - start.y; |
||||
|
setMarginBottom(baseMarginBottom - delta); |
||||
|
}; |
||||
|
const handleTouchEnd = (e) => { |
||||
|
if (!start) return; |
||||
|
const touch = Array.from(e.changedTouches).find( |
||||
|
({ identifier }) => identifier === start.identifier |
||||
|
); |
||||
|
if (!touch) return; |
||||
|
setStart(null); |
||||
|
const delta = touch.clientY - start.y; |
||||
|
const newState = |
||||
|
state === STATES.init && delta < -100 |
||||
|
? STATES.moreFac |
||||
|
: state === STATES.moreFac && delta > 100 |
||||
|
? STATES.init |
||||
|
: state; |
||||
|
|
||||
|
if (state === newState) { |
||||
|
setdoTransition(true); |
||||
|
setMarginBottom(baseMarginBottom); |
||||
|
setTimeout(() => { |
||||
|
setdoTransition(false); |
||||
|
}, 500); |
||||
|
} else setState(newState); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
{state === STATES.init || state === STATES.moreFac ? ( |
||||
|
<div |
||||
|
className={"dp" + (state === STATES.moreFac ? " more-fac" : "")} |
||||
|
style={{ |
||||
|
marginBottom: marginBottom + "px", |
||||
|
transition: !doTransition ? "none" : "margin-bottom 0.5s ease", |
||||
|
}} |
||||
|
onTouchStart={handleTouchStart} |
||||
|
onTouchMove={handleTouchMove} |
||||
|
onTouchCancel={handleTouchEnd} |
||||
|
onTouchEnd={handleTouchEnd} |
||||
|
> |
||||
|
<div |
||||
|
className="ts" |
||||
|
onClick={() => |
||||
|
setState(state === STATES.moreFac ? STATES.init : STATES.moreFac) |
||||
|
} |
||||
|
> |
||||
|
<img className="t1" src={arrow} /> |
||||
|
</div> |
||||
|
|
||||
|
<div className="search" onClick={onSearch}> |
||||
|
<img className="icon" src={search}></img> |
||||
|
<div className="sep"></div> |
||||
|
搜索店铺 |
||||
|
</div> |
||||
|
<div className="facs"> |
||||
|
{Object.entries(facilities).map(([name, list]) => ( |
||||
|
<div |
||||
|
className="fac" |
||||
|
key={name} |
||||
|
onClick={() => { |
||||
|
list.sort((a, b) => a.floorOrder - b.floorOrder); |
||||
|
setFacList(list); |
||||
|
setState(STATES.facList); |
||||
|
}} |
||||
|
> |
||||
|
<img src={list[0].url}></img> |
||||
|
{name} |
||||
|
</div> |
||||
|
))} |
||||
|
</div> |
||||
|
</div> |
||||
|
) : ( |
||||
|
<> |
||||
|
<img |
||||
|
src={close} |
||||
|
className="close" |
||||
|
onClick={(e) => { |
||||
|
e.preventDefault(); |
||||
|
e.stopPropagation(); |
||||
|
setState(STATES.init); |
||||
|
setFacList(null); |
||||
|
blurMap(); |
||||
|
}} |
||||
|
></img> |
||||
|
<div className="dp1"> |
||||
|
{facList && |
||||
|
facList.map((fac, i) => { |
||||
|
const showRow1 = |
||||
|
i === 0 || fac.floorName !== facList[i - 1].floorName; |
||||
|
const isActive = focused === fac.id; |
||||
|
return ( |
||||
|
<div key={fac.id}> |
||||
|
{showRow1 && ( |
||||
|
<div className={"row1" + (i === 0 ? "" : " has-border")}> |
||||
|
{fac.floorName} |
||||
|
</div> |
||||
|
)} |
||||
|
<div |
||||
|
className={[ |
||||
|
"row2", |
||||
|
isActive ? "active" : "", |
||||
|
!showRow1 ? "has-margin" : "", |
||||
|
].join(" ")} |
||||
|
onClick={() => { |
||||
|
setFocused(fac.id); |
||||
|
onClickFac(fac.id); |
||||
|
}} |
||||
|
> |
||||
|
{fac.name} |
||||
|
<span className="meta">{fac.floorName}</span> |
||||
|
{isActive && ( |
||||
|
<div |
||||
|
className="go" |
||||
|
src={go} |
||||
|
onClick={(e) => { |
||||
|
e.stopPropagation(); |
||||
|
setState(STATES.init); |
||||
|
onClickFac(fac.id); |
||||
|
}} |
||||
|
> |
||||
|
GO |
||||
|
</div> |
||||
|
)} |
||||
|
</div> |
||||
|
</div> |
||||
|
); |
||||
|
})} |
||||
|
</div> |
||||
|
</> |
||||
|
)} |
||||
|
{state !== STATES.facList && ( |
||||
|
<div |
||||
|
className="sf" |
||||
|
onClick={() => { |
||||
|
setState(STATES.moreFac); |
||||
|
}} |
||||
|
> |
||||
|
<ShopsWithFilter |
||||
|
mall={mall} |
||||
|
onClick={(e) => { |
||||
|
setState(STATES.init); |
||||
|
onClick(e); |
||||
|
}} |
||||
|
wingHeight="calc(100vh - 400px)" |
||||
|
></ShopsWithFilter> |
||||
|
</div> |
||||
|
)} |
||||
|
</> |
||||
|
); |
||||
|
}; |
||||
|
export default DefaultPopup; |
||||
|
After Width: | Height: | Size: 526 B |
|
After Width: | Height: | Size: 596 B |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,230 @@ |
|||||
|
.dp { |
||||
|
position: relative; |
||||
|
width: calc(100vw - 20px); |
||||
|
margin-left: 10px; |
||||
|
height: 179px; |
||||
|
border-top-left-radius: 24px; |
||||
|
border-top-right-radius: 24px; |
||||
|
text-align: center; |
||||
|
position: relative; |
||||
|
padding: 14px 0 0 0; |
||||
|
background: #ffffff; |
||||
|
box-shadow: 0px 12px 16px rgba(104, 110, 127, 0.08); |
||||
|
border-radius: 18px; |
||||
|
&::before { |
||||
|
position: absolute; |
||||
|
content: ""; |
||||
|
top: 0; |
||||
|
left: -10px; |
||||
|
right: -10px; |
||||
|
height: 236px; |
||||
|
background: linear-gradient( |
||||
|
180deg, |
||||
|
rgba(243, 244, 248, 0) 0%, |
||||
|
#f3f4f8 41.53%, |
||||
|
#f3f4f8 100% |
||||
|
); |
||||
|
z-index: -1; |
||||
|
} |
||||
|
&::after { |
||||
|
position: absolute; |
||||
|
content: ""; |
||||
|
left: -10px; |
||||
|
right: -10px; |
||||
|
top: 236px; |
||||
|
height: calc(100vh - 52px - 236px); |
||||
|
background: #f3f4f8; |
||||
|
z-index: -1; |
||||
|
} |
||||
|
&.more-fac { |
||||
|
.t1, |
||||
|
.t2 { |
||||
|
transform: rotate(180deg); |
||||
|
} |
||||
|
.facs { |
||||
|
max-height: 333px; |
||||
|
overflow-y: scroll; |
||||
|
} |
||||
|
} |
||||
|
.ts { |
||||
|
position: absolute; |
||||
|
margin: auto; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
top: 0; |
||||
|
width: 24px; |
||||
|
height: 24px; |
||||
|
.t1 { |
||||
|
position: absolute; |
||||
|
margin: auto; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
top: 3px; |
||||
|
width: 33px; |
||||
|
height: 7px; |
||||
|
border-radius: 5px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.search { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin: 0 14px; |
||||
|
height: 60px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 16px; |
||||
|
color: #323337; |
||||
|
text-align: left; |
||||
|
background: #f3f4f8; |
||||
|
border: 1px solid #edeff3; |
||||
|
box-sizing: border-box; |
||||
|
border-radius: 10px; |
||||
|
padding-left: 14px; |
||||
|
.icon { |
||||
|
width: 24px; |
||||
|
height: 24px; |
||||
|
} |
||||
|
.sep { |
||||
|
width: 1px; |
||||
|
height: 16px; |
||||
|
background: #c9cbd1; |
||||
|
margin: 0 14px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.facs { |
||||
|
display: inline-flex; |
||||
|
align-items: flex-start; |
||||
|
overflow-x: auto; |
||||
|
overflow-y: hidden; |
||||
|
width: 100%; |
||||
|
white-space: nowrap; |
||||
|
margin-top: 20px; |
||||
|
padding-left: 20px; |
||||
|
height: 75px; |
||||
|
.fac { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: space-between; |
||||
|
height: 65px; |
||||
|
margin-right: 20px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 11px; |
||||
|
line-height: 15px; |
||||
|
text-align: center; |
||||
|
color: #474a56; |
||||
|
img { |
||||
|
display: block; |
||||
|
width: 44px; |
||||
|
height: 44px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.close { |
||||
|
position: absolute; |
||||
|
top: 16px; |
||||
|
right: 26px; |
||||
|
width: 24px; |
||||
|
height: 24px; |
||||
|
z-index: 10; |
||||
|
} |
||||
|
.dp1 { |
||||
|
position: relative; |
||||
|
margin: 0 10px 26px 10px; |
||||
|
height: 326px; |
||||
|
border-radius: 18px; |
||||
|
background: #ffffff; |
||||
|
box-shadow: 0px 12px 16px rgba(104, 110, 127, 0.08); |
||||
|
text-align: center; |
||||
|
padding: 14px; |
||||
|
overflow-x: hidden; |
||||
|
overflow-y: auto; |
||||
|
.row1, |
||||
|
.row2 { |
||||
|
text-align: left; |
||||
|
line-height: 48px; |
||||
|
height: 48px; |
||||
|
} |
||||
|
.row1 { |
||||
|
font-family: DINPro; |
||||
|
font-style: normal; |
||||
|
font-weight: bold; |
||||
|
font-size: 24px; |
||||
|
line-height: 48px; |
||||
|
height: 48px; |
||||
|
color: #323337; |
||||
|
padding: 0 16px; |
||||
|
&.has-border { |
||||
|
position: relative; |
||||
|
padding-top: 24px; |
||||
|
height: 72px; |
||||
|
&::after { |
||||
|
position: absolute; |
||||
|
content: ""; |
||||
|
top: 12px; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
border-top: 1px dashed #edeff3; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.row2 { |
||||
|
display: flex; |
||||
|
position: relative; |
||||
|
padding-left: 16px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 16px; |
||||
|
color: #323337; |
||||
|
background: #f9f9fb; |
||||
|
border-radius: 12px; |
||||
|
&.has-margin { |
||||
|
margin-top: 4px; |
||||
|
} |
||||
|
&.active { |
||||
|
background: linear-gradient(180deg, #508af7 0%, #5ea5f9 100%); |
||||
|
color: #ffffff; |
||||
|
border-radius: 12px; |
||||
|
.meta { |
||||
|
color: #ffffff; |
||||
|
} |
||||
|
} |
||||
|
.meta { |
||||
|
margin-left: 16px; |
||||
|
font-family: DINPro; |
||||
|
font-style: normal; |
||||
|
font-weight: normal; |
||||
|
font-size: 17px; |
||||
|
color: #a1a5b3; |
||||
|
} |
||||
|
.go { |
||||
|
position: absolute; |
||||
|
top: 8px; |
||||
|
right: 8px; |
||||
|
width: 96px; |
||||
|
height: 32px; |
||||
|
background: #ffffff; |
||||
|
box-shadow: 0px 2px 6px rgba(93, 172, 249, 0.2); |
||||
|
border-radius: 8px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 600; |
||||
|
font-size: 16px; |
||||
|
line-height: 32px; |
||||
|
text-align: center; |
||||
|
color: #437af7; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.sf { |
||||
|
position: absolute; |
||||
|
top: 177px; |
||||
|
left: 0; |
||||
|
} |
||||
@ -0,0 +1,78 @@ |
|||||
|
import React, { useState, useEffect } from "react"; |
||||
|
import "./Floors.scss"; |
||||
|
import up from "./up.png"; |
||||
|
import loc from "./loc.png"; |
||||
|
export const upUrl = up; |
||||
|
const Floors = ({ |
||||
|
clickable, |
||||
|
floors, |
||||
|
sceneIndex, |
||||
|
setSceneIndex, |
||||
|
onClickFloor, |
||||
|
popupHeight, |
||||
|
showNav, |
||||
|
isNavEnd, |
||||
|
end, |
||||
|
hasCoupon, |
||||
|
}) => { |
||||
|
const [list, setList] = useState([]); |
||||
|
useEffect(() => { |
||||
|
setList( |
||||
|
floors |
||||
|
.map(([modelUrl, floor], i) => ({ |
||||
|
name: floor, |
||||
|
index: i, |
||||
|
modelUrl, |
||||
|
ref: React.createRef(), |
||||
|
})) |
||||
|
.filter(({ modelUrl }) => modelUrl !== null) |
||||
|
.reverse() |
||||
|
); |
||||
|
}, [floors]); |
||||
|
|
||||
|
useEffect(() => { |
||||
|
const selected = list.find(({ index }) => sceneIndex === index); |
||||
|
if (selected && selected.ref.current) |
||||
|
selected.ref.current.scrollIntoView({ |
||||
|
block: "center", |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
return !showNav ? ( |
||||
|
<div |
||||
|
className={[ |
||||
|
"floor-list", |
||||
|
showNav ? " show-nav" : "", |
||||
|
isNavEnd ? " nav-end" : "", |
||||
|
hasCoupon ? "has-coupon" : "", |
||||
|
].join(" ")} |
||||
|
style={{ bottom: popupHeight + 66 + "px" }} |
||||
|
> |
||||
|
<ul> |
||||
|
{list.map(({ name, index, ref }) => ( |
||||
|
<li |
||||
|
key={index} |
||||
|
ref={ref} |
||||
|
className={sceneIndex === index ? "active" : ""} |
||||
|
onClick={() => { |
||||
|
if (!clickable) return false; |
||||
|
setSceneIndex(index); |
||||
|
onClickFloor(index); |
||||
|
}} |
||||
|
> |
||||
|
{name} |
||||
|
</li> |
||||
|
))} |
||||
|
</ul> |
||||
|
<img alt="上箭头" className="top" src={up}></img> |
||||
|
<img alt="下箭头" className="bottom" src={up}></img> |
||||
|
</div> |
||||
|
) : ( |
||||
|
<div className="floor-sub"> |
||||
|
<img className="loc" src={loc}></img> <div className="sep"></div> |
||||
|
<div className="value">{floors[sceneIndex][1]}</div> |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default Floors; |
||||
@ -0,0 +1,95 @@ |
|||||
|
.floor-list { |
||||
|
position: absolute; |
||||
|
top: -172px; |
||||
|
left: 10px; |
||||
|
height: 160px; |
||||
|
width: 40px; |
||||
|
padding: 10px 0; |
||||
|
text-align: center; |
||||
|
box-sizing: border-box; |
||||
|
z-index: 1000; |
||||
|
background: #ffffff; |
||||
|
box-shadow: 0px 8px 8px rgba(104, 110, 127, 0.04); |
||||
|
border-radius: 12px; |
||||
|
&.has-coupon { |
||||
|
top: -211px; |
||||
|
} |
||||
|
.show-nav { |
||||
|
top: -442px; |
||||
|
} |
||||
|
&.nav-end { |
||||
|
top: -212px; |
||||
|
} |
||||
|
ul { |
||||
|
overflow-x: hidden; |
||||
|
overflow-y: auto; |
||||
|
height: 140px; |
||||
|
&::-webkit-scrollbar { |
||||
|
display: none; |
||||
|
} |
||||
|
} |
||||
|
li { |
||||
|
display: inline-block; |
||||
|
line-height: 32px; |
||||
|
font-family: DINPro; |
||||
|
font-style: normal; |
||||
|
font-weight: normal; |
||||
|
font-size: 14px; |
||||
|
text-align: center; |
||||
|
color: #323337; |
||||
|
width: 32px; |
||||
|
height: 32px; |
||||
|
margin: 4px; |
||||
|
&.active { |
||||
|
background: linear-gradient(180deg, #508af7 0%, #5ea5f9 100%); |
||||
|
border-radius: 8px; |
||||
|
color: #fff; |
||||
|
} |
||||
|
} |
||||
|
img { |
||||
|
position: absolute; |
||||
|
width: 8px; |
||||
|
height: 3px; |
||||
|
margin: auto; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
&.top { |
||||
|
top: 7px; |
||||
|
} |
||||
|
&.bottom { |
||||
|
bottom: 7px; |
||||
|
transform-origin: center; |
||||
|
transform: rotate(180deg); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.floor-sub { |
||||
|
position: absolute; |
||||
|
bottom: calc(100vh - 42px); |
||||
|
left: 10px; |
||||
|
display: inline-flex; |
||||
|
z-index: 1000; |
||||
|
height: 32px; |
||||
|
padding: 0 12px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 14px; |
||||
|
line-height: 20px; |
||||
|
color: #323337; |
||||
|
background: #fff; |
||||
|
border-radius: 20px; |
||||
|
align-content: center; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
.loc { |
||||
|
width: 16px; |
||||
|
height: 16px; |
||||
|
} |
||||
|
.sep { |
||||
|
height: 10px; |
||||
|
width: 1px; |
||||
|
margin: 0 8px; |
||||
|
background: #c9cbd1; |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 977 B |
|
After Width: | Height: | Size: 242 B |
@ -0,0 +1,184 @@ |
|||||
|
import React, { useState, useEffect } from "react"; |
||||
|
import ShopList from "../ShopList/ShopList.js"; |
||||
|
import "./HeadBar.scss"; |
||||
|
import { searchTypes } from "../Options/Options"; |
||||
|
import backpng from './back.png' |
||||
|
|
||||
|
const HeadBar = ({ |
||||
|
mall, |
||||
|
exit, |
||||
|
start, |
||||
|
end, |
||||
|
floors, |
||||
|
onSwap, |
||||
|
shop, |
||||
|
onSetStart, |
||||
|
onSetEnd, |
||||
|
isPick, |
||||
|
setIsPick, |
||||
|
blurMap, |
||||
|
showSearchType, |
||||
|
onClickSearchType, |
||||
|
searchType, |
||||
|
onIsTypingChange |
||||
|
}) => { |
||||
|
const [isTypingStart, setIsTypingStart] = useState(false); |
||||
|
const [isTypingEnd, setIsTypingEnd] = useState(false); |
||||
|
const isTyping = isTypingStart || isTypingEnd; |
||||
|
const [q, setQ] = useState(null); |
||||
|
const onlyShowStart = isTypingStart || (!start && isPick); |
||||
|
const onlyShowEnd = isTypingEnd || (!end && isPick); |
||||
|
const showStart = !onlyShowEnd; |
||||
|
const showEnd = !onlyShowStart; |
||||
|
const showBoth = !(isPick || isTyping); |
||||
|
const hasBoth = start && end; |
||||
|
const startValue = |
||||
|
q !== null ? q : isPick ? (shop ? shop.name : "") : start ? start.name : ""; |
||||
|
const endValue = |
||||
|
q !== null ? q : isPick ? (shop ? shop.name : "") : end ? end.name : ""; |
||||
|
|
||||
|
useEffect(() => { onIsTypingChange(isTyping) }, [isTyping]) |
||||
|
return ( |
||||
|
<div |
||||
|
className={"head-bar" + (showBoth ? " double" : "")} |
||||
|
onClick={() => blurMap()} |
||||
|
> |
||||
|
<div |
||||
|
className="back" |
||||
|
onClick={() => { |
||||
|
if (isPick) setIsPick(false); |
||||
|
else if (isTypingStart) { |
||||
|
setQ(null); |
||||
|
setIsTypingStart(false); |
||||
|
} else if (isTypingEnd) { |
||||
|
setQ(null); |
||||
|
setIsTypingEnd(false); |
||||
|
} else exit(); |
||||
|
}} |
||||
|
> |
||||
|
<img src={backpng}></img> |
||||
|
</div> |
||||
|
<div className="content"> |
||||
|
{showBoth && <div className="dots"></div>} |
||||
|
{showStart && ( |
||||
|
<div className={"row start" + (onlyShowStart ? " single" : "")}> |
||||
|
<div className="text"> |
||||
|
<input |
||||
|
value={startValue} |
||||
|
onFocus={() => setIsTypingStart(true)} |
||||
|
onChange={(e) => setQ(e.target.value)} |
||||
|
readOnly={isPick} |
||||
|
disabled={isPick} |
||||
|
placeholder={isPick ? "请点击地图选择起点" : "请输入起点"} |
||||
|
></input> |
||||
|
{(start || (isPick && shop)) && ( |
||||
|
<span className="label"> |
||||
|
{floors[(start ? start : shop).floorOrder][1]} |
||||
|
</span> |
||||
|
)} |
||||
|
<div style={{ flex: 1 }}></div> |
||||
|
{start === null && isPick && ( |
||||
|
<div |
||||
|
className='right' |
||||
|
onClick={() => { |
||||
|
setIsPick(false); |
||||
|
onSetStart(shop); |
||||
|
}} |
||||
|
> |
||||
|
开始导航 |
||||
|
</div> |
||||
|
)} |
||||
|
{start === null && !isPick && !isTypingStart && ( |
||||
|
<div |
||||
|
className="right" |
||||
|
onClick={() => { |
||||
|
setQ(null); |
||||
|
setIsPick(true); |
||||
|
setIsTypingStart(false); |
||||
|
setIsTypingEnd(false); |
||||
|
}} |
||||
|
> |
||||
|
地图选点 |
||||
|
</div> |
||||
|
)} |
||||
|
</div> |
||||
|
</div> |
||||
|
)} |
||||
|
{showEnd && ( |
||||
|
<div className={"row end" + (isPick ? " single" : "")}> |
||||
|
<div className={"text " + (isTypingEnd ? '' : 'has-border')}> |
||||
|
<input |
||||
|
value={endValue} |
||||
|
onFocus={() => setIsTypingEnd(true)} |
||||
|
onChange={(e) => setQ(e.target.value)} |
||||
|
readOnly={isPick} |
||||
|
disabled={isPick} |
||||
|
placeholder={isPick ? "请点击地图选择终点" : "请输入终点"} |
||||
|
></input> |
||||
|
{((isPick && shop) || end) && ( |
||||
|
<span className="label"> |
||||
|
{floors[(end ? end : shop).floorOrder][1]} |
||||
|
</span> |
||||
|
)} |
||||
|
<div style={{ flex: 1 }}></div> |
||||
|
{end === null && isPick && ( |
||||
|
<div |
||||
|
className={shop ? "right green" : "right grey"} |
||||
|
onClick={() => { |
||||
|
setIsPick(false); |
||||
|
onSetEnd(shop); |
||||
|
}} |
||||
|
> |
||||
|
开始导航 |
||||
|
</div> |
||||
|
)} |
||||
|
{end === null && !isPick && !isTypingEnd && ( |
||||
|
<div |
||||
|
className="right" |
||||
|
onClick={() => { |
||||
|
setQ(null); |
||||
|
setIsPick(true); |
||||
|
setIsTypingEnd(false); |
||||
|
}} |
||||
|
> |
||||
|
地图选点 |
||||
|
</div> |
||||
|
)} |
||||
|
</div> |
||||
|
</div> |
||||
|
)} |
||||
|
|
||||
|
{showBoth && <div className="switch" onClick={() => onSwap()}></div>} |
||||
|
</div> |
||||
|
{showBoth && !hasBoth && ( |
||||
|
<div className={"banner " + (start === null ? "start" : "end")}> |
||||
|
请在顶部选择{!start ? "起" : "终"}点 |
||||
|
</div> |
||||
|
)} |
||||
|
{isTyping && q && ( |
||||
|
<div className="shop-list-wrapper"> |
||||
|
<ShopList |
||||
|
mall={mall} |
||||
|
q={q} |
||||
|
onClick={(shop) => { |
||||
|
if (isTypingStart) { |
||||
|
onSetStart(shop); |
||||
|
setQ(null); |
||||
|
setIsTypingStart(false); |
||||
|
} |
||||
|
if (isTypingEnd) { |
||||
|
onSetEnd(shop); |
||||
|
setQ(null); |
||||
|
setIsTypingEnd(false); |
||||
|
} |
||||
|
}} |
||||
|
top={"70px"} |
||||
|
isRow={true} |
||||
|
></ShopList> |
||||
|
</div> |
||||
|
)} |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default HeadBar; |
||||
@ -0,0 +1,271 @@ |
|||||
|
.head-bar { |
||||
|
display: flex; |
||||
|
box-sizing: border-box; |
||||
|
width: calc(100vw - 20px); |
||||
|
margin-left: 10px; |
||||
|
margin-top: 10px; |
||||
|
font-size: 16px; |
||||
|
line-height: 44px; |
||||
|
position: relative; |
||||
|
z-index: 1; |
||||
|
height: 64px; |
||||
|
position: relative; |
||||
|
border-radius: 12px; |
||||
|
align-items: center; |
||||
|
pointer-events: auto; |
||||
|
&.double { |
||||
|
height: 114px; |
||||
|
.back { |
||||
|
height: 114px; |
||||
|
} |
||||
|
.content { |
||||
|
height: 114px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.back { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
position: relative; |
||||
|
background: #ffffff; |
||||
|
flex: 0 0 50px; |
||||
|
margin-right: 8px; |
||||
|
height: 64px; |
||||
|
border-radius: 12px; |
||||
|
img { |
||||
|
width: 32px; |
||||
|
height: 32px; |
||||
|
} |
||||
|
} |
||||
|
.content { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
background: #ffffff; |
||||
|
height: 64px; |
||||
|
z-index: 2; |
||||
|
flex: 1; |
||||
|
position: relative; |
||||
|
border-radius: 12px; |
||||
|
.dots { |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
left: 23px; |
||||
|
width: 2px; |
||||
|
height: 2px; |
||||
|
border-radius: 50%; |
||||
|
background: #c9cbd1; |
||||
|
margin: auto 0; |
||||
|
&::before { |
||||
|
content: ""; |
||||
|
display: block; |
||||
|
position: absolute; |
||||
|
top: -6px; |
||||
|
width: 2px; |
||||
|
height: 2px; |
||||
|
border-radius: 50%; |
||||
|
background: #c9cbd1; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
&::after { |
||||
|
content: ""; |
||||
|
display: block; |
||||
|
position: absolute; |
||||
|
bottom: -6px; |
||||
|
width: 2px; |
||||
|
height: 2px; |
||||
|
border-radius: 50%; |
||||
|
background: #c9cbd1; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
} |
||||
|
.row { |
||||
|
position: relative; |
||||
|
line-height: 57px; |
||||
|
height: 57px; |
||||
|
padding-left: 48px; |
||||
|
padding-right: 49px; |
||||
|
&.start { |
||||
|
background: 16px center/16px 16px no-repeat url("./start.png"); |
||||
|
} |
||||
|
&.end { |
||||
|
background: 16px center/16px 16px no-repeat url("./end.png"); |
||||
|
} |
||||
|
&.single { |
||||
|
padding-right: 10px; |
||||
|
} |
||||
|
.text { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
input { |
||||
|
border: none; |
||||
|
background: transparent; |
||||
|
display: inline-block; |
||||
|
line-height: 57px; |
||||
|
outline: none; |
||||
|
width: 100%; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 600; |
||||
|
font-size: 18px; |
||||
|
color: #323337; |
||||
|
} |
||||
|
::placeholder { |
||||
|
font-size: 16px; |
||||
|
font-weight: 400; |
||||
|
color: #c9cbd1; |
||||
|
} |
||||
|
&.has-border { |
||||
|
border-top: 1px solid rgba(238, 238, 238, 0.7); |
||||
|
} |
||||
|
|
||||
|
.label { |
||||
|
display: inline-block; |
||||
|
flex: 0 0 20px; |
||||
|
text-align: center; |
||||
|
margin-left: 8px; |
||||
|
font-style: normal; |
||||
|
font-weight: bold; |
||||
|
font-size: 12px; |
||||
|
color: #b3aea7; |
||||
|
} |
||||
|
.right { |
||||
|
background: #f3f4f8; |
||||
|
border: 1px solid #edeff3; |
||||
|
box-sizing: border-box; |
||||
|
border-radius: 8px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 600; |
||||
|
font-size: 12px; |
||||
|
line-height: 33px; |
||||
|
color: #437af7; |
||||
|
flex: 0 0 80px; |
||||
|
text-align: center; |
||||
|
display: inline-block; |
||||
|
white-space: nowrap; |
||||
|
margin-left: 8px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.switch { |
||||
|
position: absolute; |
||||
|
width: 49px; |
||||
|
height: 32px; |
||||
|
background-image: url("./switch.png"); |
||||
|
background-size: contain; |
||||
|
background-repeat: no-repeat; |
||||
|
background-position: center; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
right: 0; |
||||
|
margin: auto 0; |
||||
|
} |
||||
|
} |
||||
|
.banner { |
||||
|
position: absolute; |
||||
|
display: flex; |
||||
|
background: rgba(0, 0, 0, 0.6); |
||||
|
width: 237px; |
||||
|
line-height: 40px; |
||||
|
height: 40px; |
||||
|
text-align: center; |
||||
|
bottom: -48px; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
margin: auto; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: normal; |
||||
|
font-size: 14px; |
||||
|
color: #ffffff; |
||||
|
border-radius: 20px; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
&.start::before { |
||||
|
content: ""; |
||||
|
display: inline-block; |
||||
|
width: 16px; |
||||
|
height: 16px; |
||||
|
margin-right: 8px; |
||||
|
vertical-align: middle; |
||||
|
background-image: url("./start.png"); |
||||
|
background-size: contain; |
||||
|
} |
||||
|
&.end::before { |
||||
|
content: ""; |
||||
|
display: inline-block; |
||||
|
width: 16px; |
||||
|
height: 16px; |
||||
|
margin-right: 8px; |
||||
|
vertical-align: middle; |
||||
|
background-image: url("./end.png"); |
||||
|
background-size: contain; |
||||
|
} |
||||
|
&::after { |
||||
|
content: ""; |
||||
|
display: inline-block; |
||||
|
margin-left: 8px; |
||||
|
width: 16px; |
||||
|
height: 16px; |
||||
|
vertical-align: middle; |
||||
|
background-image: url("./arrow.png"); |
||||
|
background-size: contain; |
||||
|
} |
||||
|
} |
||||
|
.shop-list-wrapper { |
||||
|
position: absolute; |
||||
|
top: 64px; |
||||
|
left: -10px; |
||||
|
width: 100vw; |
||||
|
height: calc(100vh - 74px); |
||||
|
overflow-x: hidden; |
||||
|
overflow-y: auto; |
||||
|
} |
||||
|
.search-type { |
||||
|
position: absolute; |
||||
|
top: 100px; |
||||
|
width: 100vw; |
||||
|
height: 40px; |
||||
|
line-height: 40px; |
||||
|
background: #fff; |
||||
|
display: flex; |
||||
|
color: #404040; |
||||
|
left: 0; |
||||
|
font-size: 12px; |
||||
|
.el { |
||||
|
flex: 1 1 33%; |
||||
|
text-align: center; |
||||
|
span { |
||||
|
position: relative; |
||||
|
display: inline-block; |
||||
|
img { |
||||
|
width: 18px; |
||||
|
height: 18px; |
||||
|
vertical-align: middle; |
||||
|
margin-right: 8px; |
||||
|
} |
||||
|
&.active { |
||||
|
color: #0074ed; |
||||
|
} |
||||
|
&.active { |
||||
|
&::after { |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
width: 100%; |
||||
|
height: 4px; |
||||
|
background: #0074ed; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 316 B |
|
After Width: | Height: | Size: 841 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 791 B |
@ -0,0 +1,260 @@ |
|||||
|
import React, { useState, useEffect, useContext } from "react"; |
||||
|
import { ListView } from "antd-mobile"; |
||||
|
import Modal from "react-modal"; |
||||
|
import "./Malls.scss"; |
||||
|
import search from "./search.png"; |
||||
|
import pos from "./pos.png"; |
||||
|
import close_white from "./close_white.png"; |
||||
|
import up from "./up.png"; |
||||
|
import { |
||||
|
Mall, |
||||
|
cityMallsGetter, |
||||
|
CityMall, |
||||
|
} from "../../js/helpers/data-helper.js"; |
||||
|
import { MallCode } from "../../pages/Index/Index"; |
||||
|
|
||||
|
const getSectionData = (dataBlob, sectionID) => dataBlob[sectionID]; |
||||
|
const getRowData = (dataBlob, sectionID, rowID) => dataBlob[rowID]; |
||||
|
|
||||
|
function genData(ds, cityMalls) { |
||||
|
const dataBlob = {}; |
||||
|
const sectionIDs = []; |
||||
|
const rowIDs = []; |
||||
|
const groupByIndex = (list) => |
||||
|
list.reduce((acc, nxt) => { |
||||
|
acc[nxt.index] = acc[nxt.index] ? [...acc[nxt.index], nxt] : [nxt]; |
||||
|
return acc; |
||||
|
}, {}); |
||||
|
const data = groupByIndex(cityMalls); |
||||
|
Object.keys(data).forEach((item, index) => { |
||||
|
sectionIDs.push(item); |
||||
|
dataBlob[item] = item; |
||||
|
rowIDs[index] = []; |
||||
|
|
||||
|
data[item].forEach((city) => { |
||||
|
rowIDs[index].push(city.name); |
||||
|
dataBlob[city.name] = city; |
||||
|
}); |
||||
|
}); |
||||
|
return ds.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs); |
||||
|
} |
||||
|
|
||||
|
const Malls = ({ isOpen, onRequestClose, setMallCode }) => { |
||||
|
const [q, setQ] = useState(""); |
||||
|
const [showList, setShowList] = useState(false); |
||||
|
const [cities, setCities] = useState([]); |
||||
|
const [currentMall, setCurrentMall] = useState(null); |
||||
|
const [currentCity, setCurrentCity] = useState(null); |
||||
|
const [currentMalls, setCurrentMalls] = useState([]); |
||||
|
const [isMallExpand, setIsMallExpand] = useState(false); |
||||
|
const [dataSource, setDataSource] = useState( |
||||
|
new ListView.DataSource({ |
||||
|
getRowData, |
||||
|
getSectionHeaderData: getSectionData, |
||||
|
rowHasChanged: (row1, row2) => row1 !== row2, |
||||
|
sectionHeaderHasChanged: (s1, s2) => s1 !== s2, |
||||
|
}) |
||||
|
); |
||||
|
const mallCode = useContext(MallCode); |
||||
|
const setDefaultCityMall = (list) => { |
||||
|
setCurrentCity(list[0]); |
||||
|
setCurrentMall(list[0].malls[0]); |
||||
|
setCurrentMalls(list[0].malls); |
||||
|
}; |
||||
|
|
||||
|
useEffect(() => { |
||||
|
document.title = "城市商场选择"; |
||||
|
cityMallsGetter().then((cityMalls) => { |
||||
|
setCities(cityMalls); |
||||
|
setDataSource(genData(dataSource, cityMalls)); |
||||
|
|
||||
|
if (cityMalls.length) { |
||||
|
const city = cityMalls.find(({ malls }) => |
||||
|
malls.find(({ code }) => mallCode === code) |
||||
|
); |
||||
|
if (city) { |
||||
|
setCurrentCity(city); |
||||
|
const mall = city.malls.find(({ code }) => mallCode === code); |
||||
|
if (mall) setCurrentMall(mall); |
||||
|
setCurrentMalls(city.malls); |
||||
|
} else setDefaultCityMall(cityMalls); |
||||
|
} |
||||
|
}); |
||||
|
}, [mallCode]); |
||||
|
|
||||
|
return ( |
||||
|
<Modal |
||||
|
isOpen={isOpen} |
||||
|
style={{ overlay: { zIndex: 10000, background: "#fff" } }} |
||||
|
ariaHideApp={false} |
||||
|
className="malls" |
||||
|
onRequestClose={() => onRequestClose()} |
||||
|
> |
||||
|
<div className="malls" onClick={() => setIsMallExpand(false)}> |
||||
|
<div className="input-wrapper"> |
||||
|
<input |
||||
|
value={q} |
||||
|
className="input" |
||||
|
placeholder="输入城市进行搜索" |
||||
|
onChange={(e) => setQ(e.target.value)} |
||||
|
onFocus={() => setShowList(true)} |
||||
|
onBlur={() => !q && setShowList(false)} |
||||
|
></input> |
||||
|
</div> |
||||
|
<img className="search-icon" src={search}></img> |
||||
|
{q && ( |
||||
|
<img className="close" src={close_white} onClick={() => setQ("")} /> |
||||
|
)} |
||||
|
{showList && ( |
||||
|
<div className="list"> |
||||
|
{cities |
||||
|
.filter(({ name }) => name.includes(q)) |
||||
|
.map((city) => ( |
||||
|
<div |
||||
|
className="item" |
||||
|
key={city.name} |
||||
|
onClick={() => { |
||||
|
setCurrentCity(city); |
||||
|
setCurrentMall(city.malls[0]); |
||||
|
setCurrentMalls(city.malls); |
||||
|
setShowList(false); |
||||
|
}} |
||||
|
> |
||||
|
{city.name} |
||||
|
</div> |
||||
|
))} |
||||
|
</div> |
||||
|
)} |
||||
|
{!showList && ( |
||||
|
<div className="main"> |
||||
|
<div className="r1"> |
||||
|
<div className="left"> |
||||
|
<img className="pos" src={pos}></img> |
||||
|
{currentCity && <span>{currentCity.name}</span>} |
||||
|
|
||||
|
{currentMall && <span>{currentMall.name}</span>} |
||||
|
<img className="up" src={up}></img> |
||||
|
</div> |
||||
|
<span className="right">当前定位城市</span> |
||||
|
</div> |
||||
|
<div className="r2">切换商场</div> |
||||
|
<div |
||||
|
className="malls-wrapper" |
||||
|
onClick={() => { |
||||
|
setIsMallExpand(false); |
||||
|
}} |
||||
|
> |
||||
|
<div |
||||
|
className={"malls1" + (isMallExpand ? " expand" : "")} |
||||
|
onClick={(e) => { |
||||
|
e.stopPropagation(); |
||||
|
}} |
||||
|
> |
||||
|
{currentMalls.map((mall) => ( |
||||
|
<span |
||||
|
key={mall.code} |
||||
|
className="tag" |
||||
|
onClick={() => { |
||||
|
console.log("setMallCode", mall.code); |
||||
|
setCurrentMall(mall); |
||||
|
setIsMallExpand(false); |
||||
|
setMallCode(mall.code); |
||||
|
onRequestClose(); |
||||
|
// Taro.reLaunch({
|
||||
|
// url: `/pages/index/index?mallId=${mall.id}`
|
||||
|
// });
|
||||
|
}} |
||||
|
> |
||||
|
{mall.name} |
||||
|
</span> |
||||
|
))} |
||||
|
{!isMallExpand && ( |
||||
|
<div |
||||
|
className="more" |
||||
|
onClick={(e) => { |
||||
|
e.stopPropagation(); |
||||
|
setIsMallExpand(true); |
||||
|
}} |
||||
|
> |
||||
|
更多 |
||||
|
</div> |
||||
|
)} |
||||
|
{isMallExpand && ( |
||||
|
<img |
||||
|
className="fold" |
||||
|
src={up} |
||||
|
onClick={(e) => { |
||||
|
e.stopPropagation(); |
||||
|
setIsMallExpand(false); |
||||
|
}} |
||||
|
></img> |
||||
|
)} |
||||
|
</div> |
||||
|
<ListView.IndexedList |
||||
|
dataSource={dataSource} |
||||
|
className="am-list sticky-list" |
||||
|
useBodyScroll |
||||
|
renderSectionHeader={(sectionData) => ( |
||||
|
<div> |
||||
|
<div |
||||
|
className="sticky" |
||||
|
style={{ |
||||
|
zIndex: 3, |
||||
|
}} |
||||
|
> |
||||
|
{sectionData} |
||||
|
</div> |
||||
|
</div> |
||||
|
)} |
||||
|
renderHeader={() => ( |
||||
|
<div> |
||||
|
<div className="meta">切换城市</div> |
||||
|
<div className="city-buttons"> |
||||
|
{cities.map((city) => ( |
||||
|
<div |
||||
|
key={city.name} |
||||
|
className="city-button" |
||||
|
onClick={() => { |
||||
|
setCurrentCity(city); |
||||
|
setCurrentMall(city.malls[0]); |
||||
|
setCurrentMalls(city.malls); |
||||
|
}} |
||||
|
> |
||||
|
{city.name} |
||||
|
</div> |
||||
|
))} |
||||
|
</div> |
||||
|
</div> |
||||
|
)} |
||||
|
renderRow={(rowData) => ( |
||||
|
<div |
||||
|
key={rowData.name} |
||||
|
className="city-button" |
||||
|
onClick={() => { |
||||
|
setCurrentCity(rowData); |
||||
|
setCurrentMalls(rowData.malls); |
||||
|
setCurrentMall(rowData.malls[0]); |
||||
|
}} |
||||
|
> |
||||
|
{rowData.name} |
||||
|
</div> |
||||
|
)} |
||||
|
quickSearchBarStyle={{ |
||||
|
position: "absolute", |
||||
|
top: 25, |
||||
|
}} |
||||
|
delayTime={10} |
||||
|
delayActivityIndicator={ |
||||
|
<div style={{ padding: 25, textAlign: "center" }}> |
||||
|
rendering... |
||||
|
</div> |
||||
|
} |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
)} |
||||
|
</div> |
||||
|
</Modal> |
||||
|
); |
||||
|
}; |
||||
|
export default Malls; |
||||
@ -0,0 +1,242 @@ |
|||||
|
/*postcss-pxtransform disable*/ |
||||
|
.malls { |
||||
|
position: relative; |
||||
|
width: 100vw; |
||||
|
height: 100vh; |
||||
|
box-sizing: border-box; |
||||
|
color: #5a5a5a; |
||||
|
font-family: SourceHanSansCN-Medium, SourceHanSansCN; |
||||
|
background: #fff; |
||||
|
box-sizing: border-box; |
||||
|
.search-icon { |
||||
|
position: absolute; |
||||
|
width: 13px; |
||||
|
height: 13px; |
||||
|
top: 21px; |
||||
|
left: 26px; |
||||
|
z-index: 1; |
||||
|
} |
||||
|
.close { |
||||
|
position: absolute; |
||||
|
border-radius: 50%; |
||||
|
background: #8d8d8d; |
||||
|
width: 20px; |
||||
|
height: 20px; |
||||
|
top: 17px; |
||||
|
right: 19px; |
||||
|
} |
||||
|
.input-wrapper { |
||||
|
padding: 12px 14px 0 14px; |
||||
|
.input { |
||||
|
padding: 5px 30px; |
||||
|
background: #ececec; |
||||
|
border-radius: 100px; |
||||
|
height: 30px; |
||||
|
line-height: 20px; |
||||
|
font-size: 11px; |
||||
|
box-sizing: border-box; |
||||
|
border: none; |
||||
|
width: 100%; |
||||
|
outline: none; |
||||
|
} |
||||
|
::placeholder { |
||||
|
color: #a9a9a9; |
||||
|
} |
||||
|
} |
||||
|
.list { |
||||
|
padding: 0 50px 0 15px; |
||||
|
line-height: 36px; |
||||
|
height: calc(100vh - 42px); |
||||
|
font-size: 12px; |
||||
|
overflow: scroll; |
||||
|
.item { |
||||
|
border-bottom: 1px solid #f4f4f4; |
||||
|
} |
||||
|
} |
||||
|
.main { |
||||
|
.r1 { |
||||
|
padding: 18px 15px; |
||||
|
line-height: 30px; |
||||
|
.left { |
||||
|
position: relative; |
||||
|
display: inline-block; |
||||
|
font-size: 14px; |
||||
|
background: rgba(244, 244, 244, 1); |
||||
|
border-radius: 8px; |
||||
|
border: 1px solid rgba(236, 236, 236, 1); |
||||
|
padding: 0 30px; |
||||
|
.pos { |
||||
|
position: absolute; |
||||
|
width: 14px; |
||||
|
height: 16px; |
||||
|
top: 7px; |
||||
|
left: 9px; |
||||
|
} |
||||
|
.up { |
||||
|
position: absolute; |
||||
|
right: 7.5px; |
||||
|
top: 12.5px; |
||||
|
width: 8px; |
||||
|
height: 5px; |
||||
|
transform-origin: center; |
||||
|
transform: rotate(90deg); |
||||
|
} |
||||
|
|
||||
|
Text + Text { |
||||
|
margin-left: 22px; |
||||
|
} |
||||
|
} |
||||
|
.right { |
||||
|
margin-left: 15px; |
||||
|
font-size: 11px; |
||||
|
font-weight: 400; |
||||
|
color: rgba(169, 169, 169, 1); |
||||
|
} |
||||
|
} |
||||
|
.r2 { |
||||
|
padding-left: 15px; |
||||
|
color: #a9a9a9; |
||||
|
font-size: 11px; |
||||
|
line-height: 11px; |
||||
|
font-weight: 400; |
||||
|
} |
||||
|
.malls-wrapper { |
||||
|
height: calc(100vh - 119px); |
||||
|
.malls1 { |
||||
|
position: relative; |
||||
|
margin-top: 11px; |
||||
|
padding-left: 15px; |
||||
|
padding-right: 60px; |
||||
|
height: 32px; |
||||
|
overflow: hidden; |
||||
|
background: #fff; |
||||
|
z-index: 10; |
||||
|
.more { |
||||
|
position: absolute; |
||||
|
line-height: 30px; |
||||
|
color: #a9a9a9; |
||||
|
right: 14px; |
||||
|
font-size: 11px; |
||||
|
top: 0; |
||||
|
} |
||||
|
&.expand { |
||||
|
box-shadow: 0px 15px 12px 0px rgba(0, 0, 0, 0.22); |
||||
|
padding-right: 15px; |
||||
|
padding-bottom: 17px; |
||||
|
overflow: auto; |
||||
|
height: auto; |
||||
|
} |
||||
|
.tag { |
||||
|
color: #878787; |
||||
|
font-size: 12px; |
||||
|
padding: 0 11px; |
||||
|
border-radius: 15px; |
||||
|
border: 1px solid rgba(236, 236, 236, 1); |
||||
|
line-height: 30px; |
||||
|
display: inline-block; |
||||
|
margin-bottom: 12px; |
||||
|
margin-right: 10px; |
||||
|
} |
||||
|
.fold { |
||||
|
position: absolute; |
||||
|
bottom: 0; |
||||
|
width: 8px; |
||||
|
height: 5px; |
||||
|
padding: 8px 6.5px; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
margin: auto; |
||||
|
} |
||||
|
.fold::after { |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
left: -5px; |
||||
|
right: -5px; |
||||
|
top: -5px; |
||||
|
bottom: -5px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.am-indexed-list-container { |
||||
|
position: absolute; |
||||
|
top: 178px; |
||||
|
border-top: 1px solid #f4f4f4; |
||||
|
padding: 0 50px 0 15px; |
||||
|
height: calc(100vh - 178px); |
||||
|
width: 100vw; |
||||
|
box-sizing: border-box; |
||||
|
.am-indexed-list-quick-search-bar :first-child { |
||||
|
display: none !important; |
||||
|
} |
||||
|
.am-indexed-list-quick-search-bar { |
||||
|
top: 204px; |
||||
|
font-size: 11px; |
||||
|
transform: none; |
||||
|
right: 15px; |
||||
|
z-index: 1; |
||||
|
text-align: center; |
||||
|
li { |
||||
|
display: block; |
||||
|
padding: 0; |
||||
|
font-size: 11px; |
||||
|
color: #a9a9a9; |
||||
|
line-height: 15px; |
||||
|
height: 15px; |
||||
|
width: 15px; |
||||
|
} |
||||
|
li:active { |
||||
|
background: #0091ff; |
||||
|
|
||||
|
color: white; |
||||
|
border-radius: 50%; |
||||
|
} |
||||
|
} |
||||
|
.at-list::after { |
||||
|
content: none; |
||||
|
} |
||||
|
.am-indexed-list-section-body { |
||||
|
line-height: 36px; |
||||
|
color: #878787; |
||||
|
font-size: 12px; |
||||
|
font-weight: 400; |
||||
|
border-bottom: 1px solid #f4f4f4; |
||||
|
background: none; |
||||
|
padding: 0; |
||||
|
} |
||||
|
.am-indexed-list-section-header { |
||||
|
font-size: 12px; |
||||
|
font-weight: 400; |
||||
|
border-bottom: 1px solid #f4f4f4; |
||||
|
padding: 0; |
||||
|
} |
||||
|
.am-list-body { |
||||
|
color: #5a5a5a; |
||||
|
line-height: 36px; |
||||
|
} |
||||
|
} |
||||
|
.meta { |
||||
|
font-size: 11px; |
||||
|
font-weight: 400; |
||||
|
margin-top: 7px; |
||||
|
padding: 11px 0; |
||||
|
color: #a9a9a9; |
||||
|
} |
||||
|
.city-buttons { |
||||
|
display: grid; |
||||
|
grid-column-gap: 10px; |
||||
|
grid-row-gap: 11px; |
||||
|
grid-auto-rows: 30px; |
||||
|
grid-auto-columns: 70px; |
||||
|
grid-template-columns: 1fr 1fr 1fr 1fr; |
||||
|
.city-button { |
||||
|
display: inline-block; |
||||
|
color: #878787; |
||||
|
font-size: 12px; |
||||
|
line-height: 30px; |
||||
|
background: rgba(244, 244, 244, 1); |
||||
|
border-radius: 4px; |
||||
|
text-align: center; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 833 B |
|
After Width: | Height: | Size: 998 B |
|
After Width: | Height: | Size: 936 B |
|
After Width: | Height: | Size: 464 B |
|
After Width: | Height: | Size: 870 B |
|
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,53 @@ |
|||||
|
import React from "react"; |
||||
|
import { searchTypes, audioOptions } from "../Options/Options"; |
||||
|
import "./More.scss"; |
||||
|
import flat from "./2d.png"; |
||||
|
import thrD from "./3d.png"; |
||||
|
|
||||
|
const displayModes = [ |
||||
|
{ |
||||
|
id: 0, |
||||
|
name: "2D", |
||||
|
bg: flat, |
||||
|
}, |
||||
|
{ |
||||
|
id: 1, |
||||
|
name: "3D", |
||||
|
bg: thrD, |
||||
|
}, |
||||
|
]; |
||||
|
const More = ({ |
||||
|
showHeadBar, |
||||
|
displayMode, |
||||
|
searchType, |
||||
|
onClickDisplayMode, |
||||
|
onClickSearchType, |
||||
|
}) => { |
||||
|
return ( |
||||
|
<div className={"more " + (showHeadBar ? "has-header-top" : "")}> |
||||
|
<div className="types"> |
||||
|
{searchTypes.map(({ id, name, bg, bgb }) => ( |
||||
|
<div |
||||
|
key={id} |
||||
|
className={"btn " + (searchType === id ? "active" : "")} |
||||
|
onClick={() => { |
||||
|
onClickSearchType(id); |
||||
|
}} |
||||
|
> |
||||
|
<img src={searchType === id ? bgb : bg} /> |
||||
|
<div>{name}</div> |
||||
|
</div> |
||||
|
))} |
||||
|
</div> |
||||
|
|
||||
|
<div |
||||
|
className="btn big" |
||||
|
onClick={() => onClickDisplayMode(displayMode == 0 ? 1 : 0)} |
||||
|
> |
||||
|
<img src={displayMode == 0 ? flat : thrD}></img> |
||||
|
{displayMode == 0 ? "2D" : "3D"} |
||||
|
</div> |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
export default More; |
||||
@ -0,0 +1,53 @@ |
|||||
|
.more { |
||||
|
position: absolute; |
||||
|
top: 10px; |
||||
|
right: 10px; |
||||
|
display: inline-flex; |
||||
|
flex-direction: column; |
||||
|
pointer-events: auto; |
||||
|
&.has-header-top { |
||||
|
top: 138px; |
||||
|
} |
||||
|
.types { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
width: 40px; |
||||
|
height: 160px; |
||||
|
background: #ffffff; |
||||
|
box-shadow: 0px 8px 8px rgba(104, 110, 127, 0.04); |
||||
|
border-radius: 12px; |
||||
|
justify-content: space-evenly; |
||||
|
align-items: center; |
||||
|
} |
||||
|
.btn { |
||||
|
display: inline-flex; |
||||
|
flex-direction: column; |
||||
|
width: 32px; |
||||
|
height: 48px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 10px; |
||||
|
line-height: 14px; |
||||
|
text-align: center; |
||||
|
color: #474a56; |
||||
|
background: #ffffff; |
||||
|
border-radius: 12px; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
img { |
||||
|
width: 24px; |
||||
|
height: 24px; |
||||
|
} |
||||
|
&.active { |
||||
|
background: linear-gradient(180deg, #508af7 0%, #5ea5f9 100%); |
||||
|
color: #fff; |
||||
|
} |
||||
|
&.big { |
||||
|
width: 40px; |
||||
|
height: 56px; |
||||
|
margin-top: 8px; |
||||
|
box-shadow: 0px 8px 8px rgba(104, 110, 127, 0.04); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,180 @@ |
|||||
|
import React, { useState, useEffect } from "react"; |
||||
|
import "./NavBottom.scss"; |
||||
|
import followDirection from "./followDirection.png"; |
||||
|
import notFollowDirection from "./notFollowDirection.png"; |
||||
|
import play from "./play.png"; |
||||
|
import pause from "./pause.png"; |
||||
|
import closeButton from "./close.png"; |
||||
|
import left from "./left.png"; |
||||
|
import right from "./right.png"; |
||||
|
import straight from "./straight.png"; |
||||
|
import back from "./back.png"; |
||||
|
import bubble from "./bubble.png"; |
||||
|
import arpng from "./ar.png"; |
||||
|
import nav from "./nav.png"; |
||||
|
import navActive from "./nav_active.png"; |
||||
|
import des from "./des.png"; |
||||
|
import desActive from "./des_active.png"; |
||||
|
|
||||
|
const getImgByRouteSearchText = (txt) => |
||||
|
txt.includes("左前") |
||||
|
? left |
||||
|
: txt.includes("右前") |
||||
|
? right |
||||
|
: txt.includes("左后") |
||||
|
? left |
||||
|
: txt.includes("右后") |
||||
|
? right |
||||
|
: txt.includes("左转") |
||||
|
? left |
||||
|
: txt.includes("右转") |
||||
|
? right |
||||
|
: txt.includes("上") |
||||
|
? straight |
||||
|
: txt.includes("下") |
||||
|
? back |
||||
|
: straight; |
||||
|
|
||||
|
const NavBottom = ({ |
||||
|
routeSearchAnimationType, |
||||
|
switchType, |
||||
|
onExit, |
||||
|
end, |
||||
|
meters, |
||||
|
minutes, |
||||
|
paused, |
||||
|
puaseOrResume, |
||||
|
hidePause, |
||||
|
hideSwitch, |
||||
|
routeSearchText, |
||||
|
percent, |
||||
|
handleAR, |
||||
|
}) => { |
||||
|
const [showText, setShowText] = useState(false); |
||||
|
const [showNav, setShowNav] = useState(true); |
||||
|
useEffect(() => { |
||||
|
setShowText(true); |
||||
|
let timeout = setTimeout(() => { |
||||
|
clearTimeout(timeout); |
||||
|
setShowText(false); |
||||
|
}, 3000); |
||||
|
return () => { |
||||
|
clearTimeout(timeout); |
||||
|
setShowText(false); |
||||
|
}; |
||||
|
}, [routeSearchAnimationType]); |
||||
|
let progressNum = parseInt(percent * 100); |
||||
|
if (isNaN(progressNum)) return; |
||||
|
if (progressNum < 0) progressNum = 0; |
||||
|
if (progressNum > 100) progressNum = 100; |
||||
|
const progressText = progressNum + "%"; |
||||
|
|
||||
|
return ( |
||||
|
<div className={"nav-bottom" + (hidePause ? " hide-pause" : "")}> |
||||
|
<div className="tabs"> |
||||
|
<div |
||||
|
className={["tab", showNav ? "active" : ""].join(" ")} |
||||
|
onClick={() => setShowNav(true)} |
||||
|
> |
||||
|
<img src={showNav ? navActive : nav}></img> |
||||
|
导航 |
||||
|
</div> |
||||
|
<div |
||||
|
className={["tab", !showNav ? "active" : ""].join(" ")} |
||||
|
onClick={() => setShowNav(false)} |
||||
|
> |
||||
|
<img src={showNav ? des : desActive}></img> |
||||
|
目的地 |
||||
|
</div> |
||||
|
</div> |
||||
|
{showText && !hideSwitch && ( |
||||
|
<div className="switch"> |
||||
|
{routeSearchAnimationType === 1 |
||||
|
? "已切转地图视角" |
||||
|
: "已切转方向标视角"} |
||||
|
</div> |
||||
|
)} |
||||
|
{!hideSwitch && ( |
||||
|
<img |
||||
|
className="switch-pic" |
||||
|
src={ |
||||
|
routeSearchAnimationType === 1 |
||||
|
? notFollowDirection |
||||
|
: followDirection |
||||
|
} |
||||
|
onClick={() => { |
||||
|
switchType(); |
||||
|
}} |
||||
|
/> |
||||
|
)} |
||||
|
<div |
||||
|
className="ar-btn" |
||||
|
onClick={() => { |
||||
|
!paused && puaseOrResume(); |
||||
|
handleAR(end); |
||||
|
}} |
||||
|
> |
||||
|
<img src={arpng} /> |
||||
|
AR导航 |
||||
|
</div> |
||||
|
{showNav ? ( |
||||
|
<div className="nav"> |
||||
|
<img |
||||
|
className="dir" |
||||
|
src={getImgByRouteSearchText(routeSearchText)} |
||||
|
alt={routeSearchText} |
||||
|
/> |
||||
|
<div className="t1">{routeSearchText}</div> |
||||
|
<div className="t2"> |
||||
|
剩余<span className="val">{meters}</span>米 {" "} |
||||
|
<span className="val">{minutes}</span>分钟 |
||||
|
</div> |
||||
|
<img |
||||
|
alt="关闭" |
||||
|
className="close-icon" |
||||
|
src={closeButton} |
||||
|
onClick={() => onExit()} |
||||
|
></img> |
||||
|
<div className="progress"></div> |
||||
|
<div |
||||
|
className="bar" |
||||
|
style={{ width: `calc((100vw - 56px) * ${progressNum / 100})` }} |
||||
|
></div> |
||||
|
|
||||
|
<div |
||||
|
className="bubble" |
||||
|
style={{ |
||||
|
left: `calc(((100vw - 56px) * ${progressNum / 100}) + 20px)`, |
||||
|
}} |
||||
|
> |
||||
|
{progressText} |
||||
|
</div> |
||||
|
{!hidePause && ( |
||||
|
<img |
||||
|
src={paused ? play : pause} |
||||
|
className="pause" |
||||
|
onClick={() => { |
||||
|
puaseOrResume(); |
||||
|
}} |
||||
|
/> |
||||
|
)} |
||||
|
</div> |
||||
|
) : ( |
||||
|
<div className="destination"> |
||||
|
{end.logoPath && <img className="avatar" src={end.logoPath} />} |
||||
|
<img |
||||
|
alt="关闭" |
||||
|
className="close-icon" |
||||
|
src={closeButton} |
||||
|
onClick={() => onExit()} |
||||
|
></img> |
||||
|
<div className="name">{end.name}</div> |
||||
|
<div className="meta"> |
||||
|
<div> {end.floorName}</div> <div>{end.shopFormat}</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
)} |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
export default NavBottom; |
||||
@ -0,0 +1,335 @@ |
|||||
|
.nav-bottom { |
||||
|
position: absolute; |
||||
|
width: calc(100vw - 20px); |
||||
|
left: 8px; |
||||
|
bottom: 26px; |
||||
|
border-radius: 18px; |
||||
|
height: 200px; |
||||
|
padding: 14px 0 0 14px; |
||||
|
background: #fff; |
||||
|
z-index: 30; |
||||
|
.tabs { |
||||
|
position: absolute; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
bottom: 14px; |
||||
|
left: 14px; |
||||
|
right: 14px; |
||||
|
height: 56px; |
||||
|
background: #f3f4f8; |
||||
|
border-radius: 10px; |
||||
|
.tab { |
||||
|
display: flex; |
||||
|
width: calc((100vw - 20px - 28px - 12px) / 2); |
||||
|
height: 48px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 16px; |
||||
|
line-height: 22px; |
||||
|
color: #323337; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
img { |
||||
|
width: 20px; |
||||
|
height: 20px; |
||||
|
margin-right: 8px; |
||||
|
} |
||||
|
&.active { |
||||
|
background: linear-gradient(180deg, #508af7 0%, #5ea5f9 100%); |
||||
|
box-shadow: 0px 6px 12px rgba(93, 172, 249, 0.2); |
||||
|
border-radius: 10px; |
||||
|
color: #fff; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.nav { |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
top: 0; |
||||
|
bottom: 82px; |
||||
|
.dir { |
||||
|
position: absolute; |
||||
|
top: 14px; |
||||
|
left: 14px; |
||||
|
width: 80px; |
||||
|
height: 80px; |
||||
|
} |
||||
|
.t1 { |
||||
|
position: absolute; |
||||
|
top: 24px; |
||||
|
left: 108px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 600; |
||||
|
font-size: 24px; |
||||
|
line-height: 34px; |
||||
|
color: #323337; |
||||
|
} |
||||
|
.t2 { |
||||
|
position: absolute; |
||||
|
display: flex; |
||||
|
top: 65px; |
||||
|
left: 108px; |
||||
|
right: 10px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 14px; |
||||
|
line-height: 20px; |
||||
|
color: #a1a5b3; |
||||
|
align-items: center; |
||||
|
.val { |
||||
|
font-family: DINPro; |
||||
|
font-style: normal; |
||||
|
font-weight: normal; |
||||
|
font-size: 18px; |
||||
|
line-height: 23px; |
||||
|
color: #7a7e8d; |
||||
|
margin: 0 4px; |
||||
|
} |
||||
|
} |
||||
|
.exit { |
||||
|
position: absolute; |
||||
|
top: 20px; |
||||
|
right: 12px; |
||||
|
width: 48px; |
||||
|
height: 48px; |
||||
|
text-align: center; |
||||
|
border: 1px solid #e2e1df; |
||||
|
box-sizing: border-box; |
||||
|
border-radius: 12px; |
||||
|
font-style: normal; |
||||
|
font-weight: normal; |
||||
|
font-size: 14px; |
||||
|
line-height: 48px; |
||||
|
color: #6a6665; |
||||
|
} |
||||
|
.progress { |
||||
|
position: absolute; |
||||
|
left: 20px; |
||||
|
right: 20px; |
||||
|
top: 108px; |
||||
|
height: 4px; |
||||
|
background: #edeff3; |
||||
|
border-radius: 5px; |
||||
|
z-index: 1; |
||||
|
} |
||||
|
.bar { |
||||
|
position: absolute; |
||||
|
left: 20px; |
||||
|
right: 20px; |
||||
|
top: 108px; |
||||
|
height: 4px; |
||||
|
background: #437af7; |
||||
|
border-radius: 5px; |
||||
|
z-index: 2; |
||||
|
} |
||||
|
|
||||
|
.bubble { |
||||
|
display: flex; |
||||
|
position: absolute; |
||||
|
z-index: 3; |
||||
|
width: 33px; |
||||
|
height: 14px; |
||||
|
top: 103.5px; |
||||
|
background: #ffffff; |
||||
|
text-align: center; |
||||
|
border: 2px solid #437af7; |
||||
|
border-radius: 12px; |
||||
|
font-family: DINPro; |
||||
|
font-style: normal; |
||||
|
font-weight: bold; |
||||
|
font-size: 10px; |
||||
|
color: #437af7; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
transform: translate(-50%, 0); |
||||
|
} |
||||
|
|
||||
|
.close-icon { |
||||
|
position: absolute; |
||||
|
top: 16px; |
||||
|
right: 16px; |
||||
|
width: 24px; |
||||
|
height: 24px; |
||||
|
z-index: 10; |
||||
|
} |
||||
|
.avatar { |
||||
|
width: 64px; |
||||
|
height: 64px; |
||||
|
background: rgba(255, 255, 255, 1); |
||||
|
filter: drop-shadow(0px 0px 10px rgba(220, 183, 123, 0.3)); |
||||
|
border-radius: 8px; |
||||
|
margin-right: 13px; |
||||
|
background-size: contain; |
||||
|
background-position: center; |
||||
|
background-repeat: no-repeat; |
||||
|
} |
||||
|
.popupname { |
||||
|
font-style: normal; |
||||
|
font-weight: 900; |
||||
|
font-size: 24px; |
||||
|
line-height: 34px; |
||||
|
margin-top: 4px; |
||||
|
letter-spacing: 1px; |
||||
|
color: #b38f65; |
||||
|
margin-bottom: 2px; |
||||
|
} |
||||
|
|
||||
|
.popupdesc { |
||||
|
font-style: normal; |
||||
|
font-weight: normal; |
||||
|
font-size: 14px; |
||||
|
line-height: 20px; |
||||
|
color: #6a6665; |
||||
|
} |
||||
|
|
||||
|
.popuptag { |
||||
|
border: 1px solid #e1e1e1; |
||||
|
display: inline-block; |
||||
|
line-height: 18px; |
||||
|
font-size: 11px; |
||||
|
padding: 0 8px; |
||||
|
border-radius: 10px; |
||||
|
color: #a9a9a9; |
||||
|
margin-right: 10px; |
||||
|
} |
||||
|
} |
||||
|
.destination { |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
top: 0; |
||||
|
bottom: 82px; |
||||
|
.avatar { |
||||
|
position: absolute; |
||||
|
width: 80px; |
||||
|
height: 80px; |
||||
|
left: 14px; |
||||
|
top: 14px; |
||||
|
background: #ffffff; |
||||
|
box-shadow: 0px 8px 16px rgba(104, 110, 127, 0.08); |
||||
|
border-radius: 6px; |
||||
|
} |
||||
|
|
||||
|
.close-icon { |
||||
|
position: absolute; |
||||
|
top: 16px; |
||||
|
right: 16px; |
||||
|
width: 24px; |
||||
|
height: 24px; |
||||
|
z-index: 10; |
||||
|
} |
||||
|
.name { |
||||
|
position: absolute; |
||||
|
top: 24px; |
||||
|
left: 108px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 600; |
||||
|
font-size: 24px; |
||||
|
line-height: 34px; |
||||
|
color: #323337; |
||||
|
right: 20px; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
.meta { |
||||
|
display: flex; |
||||
|
position: absolute; |
||||
|
top: 68px; |
||||
|
left: 108px; |
||||
|
right: 20px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 14px; |
||||
|
line-height: 20px; |
||||
|
color: #a1a5b3; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
} |
||||
|
.switch { |
||||
|
position: absolute; |
||||
|
top: -52px; |
||||
|
right: 0; |
||||
|
height: 40px; |
||||
|
line-height: 40px; |
||||
|
color: #fff; |
||||
|
border-radius: 85px; |
||||
|
font-size: 12px; |
||||
|
font-family: SourceHanSansCN-Regular, SourceHanSansCN; |
||||
|
font-weight: 400; |
||||
|
background: rgba(0, 0, 0, 0.2); |
||||
|
padding-left: 12px; |
||||
|
padding-right: 42px; |
||||
|
animation: 3s ease-out fadeout; |
||||
|
@keyframes fadeout { |
||||
|
from { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
to { |
||||
|
opacity: 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.switch-pic { |
||||
|
position: absolute; |
||||
|
top: -52px; |
||||
|
right: 0; |
||||
|
width: 40px; |
||||
|
height: 40px; |
||||
|
vertical-align: middle; |
||||
|
box-shadow: 0px 8px 8px rgba(104, 110, 127, 0.04); |
||||
|
} |
||||
|
.ar-btn { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
position: absolute; |
||||
|
width: 111px; |
||||
|
height: 40px; |
||||
|
left: 0; |
||||
|
top: -52px; |
||||
|
background: #ffffff; |
||||
|
border-radius: 20px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 600; |
||||
|
font-size: 16px; |
||||
|
line-height: 40px; |
||||
|
color: #474a56; |
||||
|
img { |
||||
|
width: 24px; |
||||
|
height: 24px; |
||||
|
margin-right: 8px; |
||||
|
} |
||||
|
} |
||||
|
.br { |
||||
|
margin-bottom: 8px; |
||||
|
} |
||||
|
.gold { |
||||
|
font-weight: 400; |
||||
|
color: #b48764; |
||||
|
} |
||||
|
.big { |
||||
|
font-size: 20px; |
||||
|
font-family: DINPro-Bold, DINPro; |
||||
|
font-weight: bold; |
||||
|
margin: 0 5px; |
||||
|
} |
||||
|
.pause { |
||||
|
position: absolute; |
||||
|
width: 40px; |
||||
|
height: 40px; |
||||
|
top: -100px; |
||||
|
right: 0; |
||||
|
box-shadow: 0px 8px 8px rgba(104, 110, 127, 0.04); |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 865 B |
|
After Width: | Height: | Size: 596 B |
|
After Width: | Height: | Size: 1008 B |
|
After Width: | Height: | Size: 678 B |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 805 B |
|
After Width: | Height: | Size: 480 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 928 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
@ -0,0 +1,137 @@ |
|||||
|
import React from "react"; |
||||
|
import Modal from "react-modal"; |
||||
|
import flat from "./2d.png"; |
||||
|
import thrD from "./3d.png"; |
||||
|
import on from "./on.png"; |
||||
|
import mute from "./mute.png"; |
||||
|
import ft from "./ft.png"; |
||||
|
import ftb from "./ftb.png"; |
||||
|
import ztb from "./ztb.png"; |
||||
|
import zt from "./zt.png"; |
||||
|
import shortest from "./shortest.png"; |
||||
|
import shortestb from "./shortestb.png"; |
||||
|
import ft1 from "./ft1.png"; |
||||
|
import ftb1 from "./ftb1.png"; |
||||
|
import ztb1 from "./ztb1.png"; |
||||
|
import zt1 from "./zt1.png"; |
||||
|
import shortest1 from "./shortest1.png"; |
||||
|
import shortestb1 from "./shortestb1.png"; |
||||
|
import "antd-mobile/lib/switch/style/css"; |
||||
|
import "./Options.scss"; |
||||
|
|
||||
|
export const displayModes = [ |
||||
|
{ |
||||
|
id: 0, |
||||
|
name: "2D", |
||||
|
bg: flat, |
||||
|
}, |
||||
|
{ |
||||
|
id: 1, |
||||
|
name: "3D", |
||||
|
bg: thrD, |
||||
|
}, |
||||
|
]; |
||||
|
export const searchTypes = [ |
||||
|
{ |
||||
|
id: 0, |
||||
|
name: "最佳", |
||||
|
bg: shortest, |
||||
|
bgb: shortestb, |
||||
|
bg1: shortest1, |
||||
|
bgb1: shortestb1, |
||||
|
}, |
||||
|
{ |
||||
|
id: 1, |
||||
|
name: "扶梯", |
||||
|
bg: ft, |
||||
|
bgb: ftb, |
||||
|
bg1: ft1, |
||||
|
bgb1: ftb1, |
||||
|
}, |
||||
|
{ |
||||
|
id: 2, |
||||
|
name: "直梯", |
||||
|
bg: zt, |
||||
|
bgb: ztb, |
||||
|
bg1: zt1, |
||||
|
bgb1: ztb1, |
||||
|
}, |
||||
|
]; |
||||
|
export const audioOptions = [ |
||||
|
{ |
||||
|
id: 0, |
||||
|
name: "语音", |
||||
|
bg: on, |
||||
|
}, |
||||
|
{ |
||||
|
id: 1, |
||||
|
name: "语音", |
||||
|
bg: mute, |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
const Options = ({ |
||||
|
show, |
||||
|
hide, |
||||
|
onClickDisplayMode, |
||||
|
onClickSearchType, |
||||
|
onClickPlayAudioMode, |
||||
|
showHeadBar, |
||||
|
}) => ( |
||||
|
<Modal |
||||
|
isOpen={show} |
||||
|
style={{ |
||||
|
overlay: { |
||||
|
zIndex: 10000, |
||||
|
background: "rgba(0,0,0,0)", |
||||
|
}, |
||||
|
}} |
||||
|
ariaHideApp={false} |
||||
|
className={"modal" + (showHeadBar ? " has-header-bar" : "")} |
||||
|
onRequestClose={() => hide()} |
||||
|
> |
||||
|
<div className="row"> |
||||
|
{displayModes.map(({ id, name, bg }) => ( |
||||
|
<div |
||||
|
key={id} |
||||
|
className={"col"} |
||||
|
onClick={() => { |
||||
|
onClickDisplayMode(id); |
||||
|
hide(); |
||||
|
}} |
||||
|
> |
||||
|
<div |
||||
|
className="up" |
||||
|
style={{ |
||||
|
background: `center/22px 22px no-repeat url(${bg})`, |
||||
|
}} |
||||
|
></div> |
||||
|
<div className="down">{name}</div> |
||||
|
</div> |
||||
|
))} |
||||
|
|
||||
|
<div className="col"></div> |
||||
|
</div> |
||||
|
<div className="row"> |
||||
|
{searchTypes.map(({ id, name, bg }) => ( |
||||
|
<div |
||||
|
key={id} |
||||
|
className="col" |
||||
|
onClick={() => { |
||||
|
onClickSearchType(id); |
||||
|
hide(); |
||||
|
}} |
||||
|
> |
||||
|
<div |
||||
|
className="up" |
||||
|
style={{ |
||||
|
background: `center/22px 22px no-repeat url(${bg})`, |
||||
|
}} |
||||
|
></div> |
||||
|
<div className="down">{name}</div> |
||||
|
</div> |
||||
|
))} |
||||
|
</div> |
||||
|
</Modal> |
||||
|
); |
||||
|
export default Options; |
||||
@ -0,0 +1,106 @@ |
|||||
|
.modal { |
||||
|
position: absolute; |
||||
|
width: 168px; |
||||
|
height: 136px; |
||||
|
background: #ffffff; |
||||
|
border-radius: 8px; |
||||
|
box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.08); |
||||
|
outline: none; |
||||
|
right: 59px; |
||||
|
top: 12px; |
||||
|
overflow: hidden; |
||||
|
padding-top: 16px; |
||||
|
&.has-header-bar { |
||||
|
top: 112px; |
||||
|
} |
||||
|
.title { |
||||
|
background: #f4f4f4; |
||||
|
padding: 0 19px; |
||||
|
color: #878787; |
||||
|
font-family: SourceHanSansCN-Regular, SourceHanSansCN; |
||||
|
font-weight: 400; |
||||
|
line-height: 40px; |
||||
|
font-size: 11px; |
||||
|
} |
||||
|
.row { |
||||
|
display: flex; |
||||
|
flex: 1; |
||||
|
flex-direction: row; |
||||
|
margin-bottom: 18px; |
||||
|
.col { |
||||
|
flex: 1; |
||||
|
text-align: center; |
||||
|
.up { |
||||
|
width: 22px; |
||||
|
height: 22px; |
||||
|
margin: auto; |
||||
|
} |
||||
|
.down { |
||||
|
font-size: 12px; |
||||
|
font-family: SourceHanSansCN, SourceHanSansCN-Regular; |
||||
|
font-weight: 400; |
||||
|
text-align: center; |
||||
|
color: #696969; |
||||
|
line-height: 20px; |
||||
|
} |
||||
|
&.active { |
||||
|
.down { |
||||
|
color: #0074ed; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
.bottom { |
||||
|
position: absolute; |
||||
|
bottom: 0; |
||||
|
width: 100%; |
||||
|
line-height: 52px; |
||||
|
padding: 0 20px; |
||||
|
background: #f4f4f4; |
||||
|
color: #404040; |
||||
|
font-size: 12px; |
||||
|
font-family: SourceHanSansCN-Regular, SourceHanSansCN; |
||||
|
font-weight: 400; |
||||
|
.am-switch { |
||||
|
position: absolute; |
||||
|
top: 16px; |
||||
|
right: 20px; |
||||
|
input[type="checkbox"]:checked + .checkbox:after { |
||||
|
transform: translateX(22px); |
||||
|
} |
||||
|
.checkbox { |
||||
|
width: 42px; |
||||
|
height: 20px; |
||||
|
border-radius: 20px; |
||||
|
&:before { |
||||
|
content: " "; |
||||
|
position: absolute; |
||||
|
left: 1.5px; |
||||
|
top: 1.5px; |
||||
|
width: 39px; |
||||
|
height: 17px; |
||||
|
border-radius: 17px; |
||||
|
box-sizing: border-box; |
||||
|
background: #fff; |
||||
|
z-index: 1; |
||||
|
transition: all 200ms; |
||||
|
transform: scale(1); |
||||
|
} |
||||
|
&:after { |
||||
|
content: " "; |
||||
|
height: 17px; |
||||
|
width: 17px; |
||||
|
border-radius: 17px; |
||||
|
background: #fff; |
||||
|
position: absolute; |
||||
|
z-index: 2; |
||||
|
left: 1.5px; |
||||
|
top: 1.5px; |
||||
|
transform: translateX(0); |
||||
|
transition: all 200ms; |
||||
|
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.21); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 720 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 452 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 905 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 565 B |
|
After Width: | Height: | Size: 585 B |
|
After Width: | Height: | Size: 466 B |
|
After Width: | Height: | Size: 590 B |
@ -0,0 +1,67 @@ |
|||||
|
import React from "react"; |
||||
|
import desc from "./desc.png"; |
||||
|
import closeButton from "./close.png"; |
||||
|
import "./Popup.scss"; |
||||
|
import navpng from "./nav.png"; |
||||
|
import arpng from "./ar.png"; |
||||
|
import couponTop from "./couponTop.png"; |
||||
|
const Popup = ({ |
||||
|
showPopup, |
||||
|
defaultPopup, |
||||
|
floors, |
||||
|
closePopup, |
||||
|
shop, |
||||
|
showDetail, |
||||
|
setEnd, |
||||
|
handleAR, |
||||
|
onClickCoupon, |
||||
|
}) => ( |
||||
|
<div className={["popup-wrapper", showPopup ? "" : "noshow"].join(" ")}> |
||||
|
{floors} |
||||
|
{defaultPopup} |
||||
|
{showPopup && ( |
||||
|
<div className="popup"> |
||||
|
{shop.hasCoupon && ( |
||||
|
<img |
||||
|
className="couponTop" |
||||
|
src={couponTop} |
||||
|
onClick={(shop) => onClickCoupon(shop)} |
||||
|
></img> |
||||
|
)} |
||||
|
<img |
||||
|
alt="关闭" |
||||
|
className="close-icon" |
||||
|
src={closeButton} |
||||
|
onClick={() => closePopup()} |
||||
|
></img> |
||||
|
<div className="r1"> |
||||
|
{shop.logoPath && ( |
||||
|
<div |
||||
|
className="avatar" |
||||
|
style={{ |
||||
|
backgroundImage: `url(${shop.logoPath})`, |
||||
|
}} |
||||
|
></div> |
||||
|
)} |
||||
|
<div style={{ flex: 1 }}> |
||||
|
<div className="popupname">{shop.name}</div> |
||||
|
<div className="popupdesc"> |
||||
|
<span> {shop.shopFormat ? shop.shopFormat : " "}</span> |
||||
|
<span> {shop.floorName}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div className="tabs"> |
||||
|
<div className="tab" onClick={() => setEnd()}> |
||||
|
<img src={navpng} /> 导航动画 |
||||
|
</div> |
||||
|
<div className="tab" onClick={() => handleAR(shop)}> |
||||
|
<img src={arpng} /> AR实时导航 |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
)} |
||||
|
</div> |
||||
|
); |
||||
|
export default Popup; |
||||
@ -0,0 +1,194 @@ |
|||||
|
.popup-wrapper { |
||||
|
position: absolute; |
||||
|
width: 100vw; |
||||
|
left: 0; |
||||
|
bottom: 0; |
||||
|
z-index: 10000; |
||||
|
box-sizing: border-box; |
||||
|
&.noshow { |
||||
|
padding: 0; |
||||
|
} |
||||
|
.popup { |
||||
|
position: relative; |
||||
|
width: calc(100vw - 20px); |
||||
|
margin-left: 10px; |
||||
|
margin-bottom: 26px; |
||||
|
background: #ffffff; |
||||
|
box-shadow: 0px 12px 16px rgba(104, 110, 127, 0.08); |
||||
|
border-radius: 18px; |
||||
|
height: 200px; |
||||
|
background: #fff; |
||||
|
.couponTop { |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
top: -40px; |
||||
|
z-index: -1; |
||||
|
width: 100%; |
||||
|
object-fit: contain; |
||||
|
} |
||||
|
.close-icon { |
||||
|
position: absolute; |
||||
|
top: 16px; |
||||
|
right: 16px; |
||||
|
width: 24px; |
||||
|
height: 24px; |
||||
|
z-index: 10; |
||||
|
} |
||||
|
.r1 { |
||||
|
display: flex; |
||||
|
width: 100%; |
||||
|
padding: 14px 20px 0 14px; |
||||
|
height: 110px; |
||||
|
.avatar { |
||||
|
width: 80px; |
||||
|
height: 80px; |
||||
|
background: #ffffff; |
||||
|
box-shadow: 0px 8px 16px rgba(104, 110, 127, 0.08); |
||||
|
border-radius: 6px; |
||||
|
margin-right: 22px; |
||||
|
background-size: 64px 64px; |
||||
|
background-position: center; |
||||
|
background-repeat: no-repeat; |
||||
|
} |
||||
|
.popupname { |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 600; |
||||
|
font-size: 24px; |
||||
|
line-height: 34px; |
||||
|
color: #323337; |
||||
|
margin-top: 10px; |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
|
.popupdesc { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 14px; |
||||
|
line-height: 20px; |
||||
|
color: #a1a5b3; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.tabs { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
padding: 20px 14px; |
||||
|
padding-bottom: 0; |
||||
|
&::after { |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 20px; |
||||
|
right: 20px; |
||||
|
border-top: 1px dashed #edeff3; |
||||
|
} |
||||
|
.tab { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
flex: 1; |
||||
|
height: 56px; |
||||
|
background: linear-gradient(180deg, #508af7 0%, #5ea5f9 100%); |
||||
|
border-radius: 10px; |
||||
|
text-align: center; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 600; |
||||
|
font-size: 16px; |
||||
|
line-height: 22px; |
||||
|
color: #ffffff; |
||||
|
img { |
||||
|
width: 20px; |
||||
|
height: 20px; |
||||
|
margin-right: 8px; |
||||
|
} |
||||
|
} |
||||
|
.tab + .tab { |
||||
|
margin-left: 14px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.meta { |
||||
|
line-height: 12px; |
||||
|
vertical-align: middle; |
||||
|
img { |
||||
|
width: 12px; |
||||
|
} |
||||
|
.title { |
||||
|
color: #7e7e7e; |
||||
|
font-size: 12px; |
||||
|
margin-left: 5px; |
||||
|
} |
||||
|
} |
||||
|
.detail { |
||||
|
color: #a9a9a9; |
||||
|
font-size: 12px; |
||||
|
line-height: 16px; |
||||
|
padding-right: 17px; |
||||
|
padding-bottom: 30px; |
||||
|
.title { |
||||
|
margin-bottom: 4px; |
||||
|
color: #7e7e7e; |
||||
|
font-size: 14px; |
||||
|
line-height: 14px; |
||||
|
} |
||||
|
.content { |
||||
|
max-height: 64px; |
||||
|
overflow: scroll; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.navbtn { |
||||
|
position: absolute; |
||||
|
top: 46px; |
||||
|
right: 14px; |
||||
|
text-align: center; |
||||
|
&::before { |
||||
|
content: ""; |
||||
|
display: block; |
||||
|
position: absolute; |
||||
|
top: 9px; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
margin: 0 auto; |
||||
|
width: 74px; |
||||
|
height: 30px; |
||||
|
background: linear-gradient( |
||||
|
90deg, |
||||
|
rgba(46, 177, 255, 1) 0%, |
||||
|
rgba(61, 140, 255, 1) 100% |
||||
|
); |
||||
|
border-radius: 17px; |
||||
|
opacity: 0.5; |
||||
|
filter: blur(4px); |
||||
|
z-index: -2; |
||||
|
} |
||||
|
&::after { |
||||
|
content: "去这里"; |
||||
|
display: block; |
||||
|
top: 0; |
||||
|
right: 0; |
||||
|
z-index: 2; |
||||
|
background: linear-gradient( |
||||
|
90deg, |
||||
|
rgba(46, 177, 255, 1) 0%, |
||||
|
rgba(61, 140, 255, 1) 100% |
||||
|
); |
||||
|
border-radius: 17px; |
||||
|
line-height: 34px; |
||||
|
padding: 0 18px; |
||||
|
font-size: 12px; |
||||
|
font-family: SourceHanSansCN-Medium, SourceHanSansCN; |
||||
|
font-weight: 500; |
||||
|
color: #fff; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 596 B |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 97 B |
|
After Width: | Height: | Size: 480 B |
@ -0,0 +1,40 @@ |
|||||
|
import React from "react"; |
||||
|
import "./Shop.scss"; |
||||
|
import { LazyLoadImage } from "react-lazy-load-image-component"; |
||||
|
const Shop = ({ |
||||
|
name, |
||||
|
floorName, |
||||
|
shopFormat, |
||||
|
hasBorderTop, |
||||
|
onClick, |
||||
|
logoPath, |
||||
|
isRow, |
||||
|
houseNum, |
||||
|
}) => ( |
||||
|
<div |
||||
|
onClick={() => onClick()} |
||||
|
className={ |
||||
|
"shop" + |
||||
|
(hasBorderTop ? " has-border-top" : "") + |
||||
|
(isRow ? " is-row" : "") |
||||
|
} |
||||
|
> |
||||
|
<LazyLoadImage className="avatar" src={logoPath}></LazyLoadImage> |
||||
|
{isRow ? ( |
||||
|
<> |
||||
|
<div className="name">{name}</div> |
||||
|
<div className="format">{shopFormat}</div> |
||||
|
<div className="houseNum">{houseNum}</div> |
||||
|
</> |
||||
|
) : ( |
||||
|
<> |
||||
|
<div className="r1">{name} </div> |
||||
|
<div className="r2"> |
||||
|
{shopFormat} <span>{floorName}</span> |
||||
|
</div> |
||||
|
</> |
||||
|
)} |
||||
|
</div> |
||||
|
); |
||||
|
|
||||
|
export default Shop; |
||||
@ -0,0 +1,91 @@ |
|||||
|
/*postcss-pxtransform disable*/ |
||||
|
.shop { |
||||
|
font-size: 0; |
||||
|
line-height: 0; |
||||
|
overflow: hidden; |
||||
|
&.is-row { |
||||
|
position: relative; |
||||
|
height: 66px; |
||||
|
background: #ffffff; |
||||
|
border-radius: 12px; |
||||
|
.avatar { |
||||
|
position: absolute; |
||||
|
width: 50px; |
||||
|
height: 50px; |
||||
|
top: 8px; |
||||
|
left: 8px; |
||||
|
border: 1px solid #e9d7ad; |
||||
|
box-sizing: border-box; |
||||
|
border-radius: 6px; |
||||
|
padding: 7px; |
||||
|
} |
||||
|
.name { |
||||
|
position: absolute; |
||||
|
top: 10px; |
||||
|
left: 66px; |
||||
|
font-style: normal; |
||||
|
font-weight: bold; |
||||
|
font-size: 16px; |
||||
|
line-height: 22px; |
||||
|
color: #333333; |
||||
|
} |
||||
|
.houseNum { |
||||
|
position: absolute; |
||||
|
left: 67px; |
||||
|
bottom: 13px; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 10px; |
||||
|
line-height: 13px; |
||||
|
color: #b3aea7; |
||||
|
} |
||||
|
.format { |
||||
|
position: absolute; |
||||
|
top: 11px; |
||||
|
right: 16px; |
||||
|
font-style: normal; |
||||
|
font-weight: bold; |
||||
|
font-size: 12px; |
||||
|
line-height: 17px; |
||||
|
color: #b3aea7; |
||||
|
} |
||||
|
} |
||||
|
.avatar { |
||||
|
width: calc((100vw - 96px - 18px - 10px - 11px) / 3); |
||||
|
height: calc((100vw - 96px - 18px - 10px - 11px) / 3); |
||||
|
padding: 8px; |
||||
|
background: #ffffff; |
||||
|
box-sizing: border-box; |
||||
|
border-radius: 6px; |
||||
|
} |
||||
|
.r1 { |
||||
|
padding: 0 4px; |
||||
|
margin-top: 4px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 12px; |
||||
|
line-height: 17px; |
||||
|
color: #323337; |
||||
|
overflow: hidden; |
||||
|
white-space: nowrap; |
||||
|
text-overflow: ellipsis; |
||||
|
} |
||||
|
.r2 { |
||||
|
margin-top: 4px; |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 9px; |
||||
|
line-height: 13px; |
||||
|
color: #a1a5b3; |
||||
|
padding: 0 4px; |
||||
|
span { |
||||
|
font-weight: normal; |
||||
|
font-size: 10px; |
||||
|
color: #a1a5b3; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 97 B |
@ -0,0 +1,82 @@ |
|||||
|
import React, { useState, useRef } from "react"; |
||||
|
import Shop from "../Shop/Shop"; |
||||
|
import "./ShopList.scss"; |
||||
|
import InfiniteScroll from "react-infinite-scroller"; |
||||
|
|
||||
|
const ShopList = ({ |
||||
|
mall, |
||||
|
q, |
||||
|
onClick, |
||||
|
top, |
||||
|
isRow = false, |
||||
|
format = null, |
||||
|
floorOrder = null, |
||||
|
}) => { |
||||
|
const [index, setIndex] = useState(0); |
||||
|
const shops = mall.shopInfo.reduce( |
||||
|
(acc, { shopList }) => acc.concat(shopList), |
||||
|
[] |
||||
|
); |
||||
|
const upperQ = q ? q.toUpperCase() : ""; |
||||
|
const filteredShops = shops |
||||
|
.filter(({ yaxis }) => yaxis) |
||||
|
.filter( |
||||
|
({ |
||||
|
name, |
||||
|
nameEn, |
||||
|
floorName, |
||||
|
initials, |
||||
|
spelling, |
||||
|
initialsCn, |
||||
|
initialsEn, |
||||
|
}) => |
||||
|
!q |
||||
|
? true |
||||
|
: [ |
||||
|
name, |
||||
|
nameEn, |
||||
|
floorName, |
||||
|
initials, |
||||
|
spelling, |
||||
|
initialsCn, |
||||
|
initialsEn, |
||||
|
] |
||||
|
.map( |
||||
|
(key) => |
||||
|
key && key.toUpperCase && key.toUpperCase().includes(upperQ) |
||||
|
) |
||||
|
.reduce((acc, nxt) => acc || nxt, false) |
||||
|
) |
||||
|
.filter( |
||||
|
({ shopFormat, floorOrder: floorOrder1 }) => |
||||
|
(format === null ? true : shopFormat === format) && |
||||
|
(floorOrder === null ? true : floorOrder1 === floorOrder) |
||||
|
); |
||||
|
const listRef = useRef(null); |
||||
|
return ( |
||||
|
<div className={"shop-list " + (isRow ? "is-row" : "")} ref={listRef}> |
||||
|
<InfiniteScroll |
||||
|
pageStart={0} |
||||
|
loadMore={() => setIndex(index + 1)} |
||||
|
hasMore={filteredShops && filteredShops.length > index * 10} |
||||
|
useWindow={false} |
||||
|
getScrollParent={() => listRef && listRef.current} |
||||
|
> |
||||
|
<div className="list"> |
||||
|
{filteredShops.map((shop, i) => ( |
||||
|
<Shop |
||||
|
{...shop} |
||||
|
hasBorderTop={i !== 0} |
||||
|
key={`${shop.name}_${shop.houseNum}`} |
||||
|
onClick={() => onClick(shop)} |
||||
|
isRow={isRow} |
||||
|
></Shop> |
||||
|
))} |
||||
|
</div> |
||||
|
<div className="end">END</div> |
||||
|
</InfiniteScroll> |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default ShopList; |
||||
@ -0,0 +1,70 @@ |
|||||
|
.shop-list { |
||||
|
width: 100%; |
||||
|
padding: 0 10px; |
||||
|
&.is-row { |
||||
|
padding: 0 8px; |
||||
|
.list { |
||||
|
display: grid; |
||||
|
gap: 8px; |
||||
|
grid-template-columns: 1fr; |
||||
|
} |
||||
|
} |
||||
|
.list { |
||||
|
display: grid; |
||||
|
gap: 12px 9px; |
||||
|
grid-template-columns: 1fr 1fr 1fr; |
||||
|
padding: 10px 0; |
||||
|
} |
||||
|
.search-his { |
||||
|
position: relative; |
||||
|
padding: 0 14px 0 15px; |
||||
|
color: #5a5a5a; |
||||
|
font-weight: 400; |
||||
|
font-size: 12px; |
||||
|
line-height: 45px; |
||||
|
background: #fff; |
||||
|
.button { |
||||
|
float: right; |
||||
|
color: #4d9fdd; |
||||
|
} |
||||
|
&::after { |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
display: block; |
||||
|
width: calc(100vw - 29px); |
||||
|
bottom: 0; |
||||
|
border-top: 1px solid #f7f7f7; |
||||
|
} |
||||
|
} |
||||
|
.end { |
||||
|
display: flex; |
||||
|
width: 100%; |
||||
|
height: 24px; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
margin-top: 24px; |
||||
|
font-family: DINPro; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 18.5px; |
||||
|
line-height: 24px; |
||||
|
text-align: center; |
||||
|
color: #c9cbd1; |
||||
|
} |
||||
|
.end::before { |
||||
|
content: ""; |
||||
|
display: inline-block; |
||||
|
width: 84px; |
||||
|
border-top: 1px solid #dedede; |
||||
|
vertical-align: middle; |
||||
|
margin-right: 10px; |
||||
|
} |
||||
|
.end::after { |
||||
|
content: ""; |
||||
|
display: inline-block; |
||||
|
width: 84px; |
||||
|
border-top: 1px solid #dedede; |
||||
|
vertical-align: middle; |
||||
|
margin-left: 10px; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
import React from "react"; |
||||
|
import "./ShopTabs.scss"; |
||||
|
import floor from "./floor.png"; |
||||
|
import floorActive from "./floor_active.png"; |
||||
|
import format from "./format.png"; |
||||
|
import formatActive from "./format_active.png"; |
||||
|
export default ({ tab, onSetTab }) => ( |
||||
|
<div className="shop-tabs"> |
||||
|
<div |
||||
|
onClick={() => onSetTab("业态")} |
||||
|
className={["tab", tab === "业态" ? "active" : ""].join(" ")} |
||||
|
> |
||||
|
<img src={tab === "业态" ? formatActive : format}></img> |
||||
|
业态 |
||||
|
</div> |
||||
|
<div |
||||
|
onClick={() => onSetTab("楼层")} |
||||
|
className={["tab", tab === "楼层" ? "active" : ""].join(" ")} |
||||
|
> |
||||
|
<img src={tab === "楼层" ? floorActive : floor}></img> |
||||
|
楼层 |
||||
|
</div> |
||||
|
</div> |
||||
|
); |
||||
@ -0,0 +1,32 @@ |
|||||
|
.shop-tabs { |
||||
|
display: flex; |
||||
|
width: calc(100vw - 20px); |
||||
|
height: 56px; |
||||
|
background: #edeff3; |
||||
|
border-radius: 12px; |
||||
|
justify-content: space-around; |
||||
|
align-items: center; |
||||
|
.tab { |
||||
|
display: flex; |
||||
|
width: calc((100vw - 20px - 8px - 3px) / 2); |
||||
|
height: 48px; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 16px; |
||||
|
line-height: 22px; |
||||
|
color: #7a7e8d; |
||||
|
&.active { |
||||
|
background: #ffffff; |
||||
|
border-radius: 8px; |
||||
|
color: #323337; |
||||
|
} |
||||
|
img { |
||||
|
width: 20px; |
||||
|
height: 20px; |
||||
|
margin-right: 8px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
After Width: | Height: | Size: 901 B |
|
After Width: | Height: | Size: 858 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,103 @@ |
|||||
|
import React, { useState, useEffect } from "react"; |
||||
|
import ShopList from "../ShopList/ShopList.js"; |
||||
|
import "./ShopsWithFilter.scss"; |
||||
|
import ShopTabs from "../ShopTabs/ShopTabs"; |
||||
|
import SideBar from "../SideBar/SideBar"; |
||||
|
const ShopsWithFilter = ({ |
||||
|
mall, |
||||
|
onClick, |
||||
|
reset, |
||||
|
onReset = () => {}, |
||||
|
wingHeight, |
||||
|
}) => { |
||||
|
const [tab, setTab] = useState("业态"); |
||||
|
const [format, setFormat] = useState(null); |
||||
|
const [floorOrder, setFloorOrder] = useState(null); |
||||
|
useEffect(() => { |
||||
|
if (reset) { |
||||
|
setFormat(null); |
||||
|
setFloorOrder(null); |
||||
|
setTab("业态"); |
||||
|
onReset(); |
||||
|
} |
||||
|
}, [reset]); |
||||
|
const formats = mall |
||||
|
? mall.shopInfo.reduce( |
||||
|
(acc, { shopList }) => |
||||
|
shopList.reduce( |
||||
|
(acc1, nxt) => |
||||
|
acc1.includes(nxt.shopFormat) |
||||
|
? [...acc1] |
||||
|
: [...acc1, nxt.shopFormat], |
||||
|
acc |
||||
|
), |
||||
|
[] |
||||
|
) |
||||
|
: []; |
||||
|
const floors = mall ? mall.floorData : []; |
||||
|
const floorNameOrderMap = floors.reduce( |
||||
|
(acc, nxt) => ({ ...acc, [nxt.name]: nxt.floorOrder }), |
||||
|
{} |
||||
|
); |
||||
|
const floorOrderNameMap = floors.reduce( |
||||
|
(acc, nxt) => ({ ...acc, [nxt.floorOrder]: nxt.name }), |
||||
|
{} |
||||
|
); |
||||
|
return ( |
||||
|
<> |
||||
|
<div style={{ marginTop: "16px", marginLeft: "10px" }}> |
||||
|
<ShopTabs |
||||
|
tab={tab} |
||||
|
onSetTab={(el) => { |
||||
|
setFormat(null); |
||||
|
setFloorOrder(null); |
||||
|
setTab(el); |
||||
|
}} |
||||
|
></ShopTabs> |
||||
|
</div> |
||||
|
|
||||
|
<div className="wings" style={{ height: wingHeight }}> |
||||
|
<div className="left"> |
||||
|
<SideBar |
||||
|
active={ |
||||
|
tab === "业态" && format === null |
||||
|
? "全部业态" |
||||
|
: tab === "楼层" && floorOrder === null |
||||
|
? "全部楼层" |
||||
|
: tab === "业态" |
||||
|
? format |
||||
|
: floorOrderNameMap[floorOrder] |
||||
|
} |
||||
|
onSetActive={(el) => { |
||||
|
if (tab === "业态") { |
||||
|
setFormat(el === "全部业态" ? null : el); |
||||
|
} else { |
||||
|
setFloorOrder(el === "全部楼层" ? null : floorNameOrderMap[el]); |
||||
|
} |
||||
|
}} |
||||
|
list={ |
||||
|
tab === "业态" |
||||
|
? ["全部业态", ...formats] |
||||
|
: [ |
||||
|
"全部楼层", |
||||
|
...floors |
||||
|
.filter(({ url }) => url !== null) |
||||
|
.map(({ name }) => name), |
||||
|
] |
||||
|
} |
||||
|
></SideBar> |
||||
|
</div> |
||||
|
<div className="right"> |
||||
|
<ShopList |
||||
|
mall={mall} |
||||
|
isRow={false} |
||||
|
format={format} |
||||
|
floorOrder={floorOrder} |
||||
|
onClick={onClick} |
||||
|
></ShopList> |
||||
|
</div> |
||||
|
</div> |
||||
|
</> |
||||
|
); |
||||
|
}; |
||||
|
export default ShopsWithFilter; |
||||
@ -0,0 +1,22 @@ |
|||||
|
.wings { |
||||
|
display: flex; |
||||
|
width: 100vw; |
||||
|
height: calc(100vh - 156px); |
||||
|
border-top: 1px solid #edeff3; |
||||
|
margin-top: 8px; |
||||
|
.left { |
||||
|
width: 96px; |
||||
|
flex: 0 0 96px; |
||||
|
height: 100%; |
||||
|
background: #edeff3; |
||||
|
overflow-x: hidden; |
||||
|
overflow-y: auto; |
||||
|
} |
||||
|
.right { |
||||
|
flex: 1; |
||||
|
height: 100%; |
||||
|
background: #f3f4f8; |
||||
|
overflow-x: hidden; |
||||
|
overflow-y: auto; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
import React from "react"; |
||||
|
import "./SideBar.scss"; |
||||
|
export default ({ list, active, onSetActive }) => ( |
||||
|
<div className="side-bar"> |
||||
|
{list.map((el) => ( |
||||
|
<div |
||||
|
key={el} |
||||
|
className={["option", active === el ? "active" : ""].join(" ")} |
||||
|
onClick={() => onSetActive(el)} |
||||
|
> |
||||
|
{el} |
||||
|
</div> |
||||
|
))} |
||||
|
</div> |
||||
|
); |
||||
@ -0,0 +1,20 @@ |
|||||
|
.side-bar { |
||||
|
width: 100%; |
||||
|
.option { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
width: 100%; |
||||
|
height: 48px; |
||||
|
font-family: PingFang SC; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
font-size: 12px; |
||||
|
color: #474a56; |
||||
|
&.active { |
||||
|
background: #ffffff; |
||||
|
color: #437af7; |
||||
|
font-weight: 600; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
import React from "react"; |
||||
|
import ReactDOM from "react-dom"; |
||||
|
import App from "./App"; |
||||
|
import reportWebVitals from "./reportWebVitals"; |
||||
|
|
||||
|
ReactDOM.render( |
||||
|
<React.StrictMode> |
||||
|
<App /> |
||||
|
</React.StrictMode>, |
||||
|
document.getElementById("root") |
||||
|
); |
||||
|
|
||||
|
// If you want to start measuring performance in your app, pass a function
|
||||
|
// to log results (for example: reportWebVitals(console.log))
|
||||
|
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
|
reportWebVitals(); |
||||