@ -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,54 @@ |
|||
{ |
|||
"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", |
|||
"html5-qrcode": "^2.3.0", |
|||
"node-sass": "^5.0.0", |
|||
"qrcodejs2": "^0.0.2", |
|||
"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,231 @@ |
|||
.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; |
|||
align-items: center; |
|||
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,198 @@ |
|||
.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; |
|||
width: calc(100vw - 187px); |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
} |
|||
|
|||
.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(); |
|||