Commit 4a33873b by zhanghaozhe

prettier

parent 212a67b6
build build
node_modules
public public
\ No newline at end of file
{ {
"semi": false
} }
\ No newline at end of file
const postcssNormalize = require('postcss-normalize') const postcssNormalize = require("postcss-normalize")
const path = require('path') const path = require("path")
module.exports = { module.exports = {
stories: ['../src/common/**/*.stories.tsx'], stories: ["../src/common/**/*.stories.tsx"],
addons: [ addons: [
{ {
name: '@storybook/preset-typescript', name: "@storybook/preset-typescript",
options: { options: {
tsLoaderOptions: { tsLoaderOptions: {
configFile: path.resolve(__dirname, '../tsconfig.json'), configFile: path.resolve(__dirname, "../tsconfig.json"),
}, },
}, },
}, },
'@storybook/addon-actions', "@storybook/addon-actions",
'@storybook/addon-links', "@storybook/addon-links",
'@storybook/addon-knobs', "@storybook/addon-knobs",
], ],
webpackFinal: async config => { webpackFinal: async (config) => {
config.module.rules.push({ config.module.rules.push({
test: /\.scss$/, test: /\.scss$/,
use: [ use: [
'style-loader', "style-loader",
'css-loader', "css-loader",
{ {
loader: require.resolve('postcss-loader'), loader: require.resolve("postcss-loader"),
options: { options: {
ident: 'postcss', ident: "postcss",
plugins: () => [ plugins: () => [
require('postcss-flexbugs-fixes'), require("postcss-flexbugs-fixes"),
require('postcss-preset-env')({ require("postcss-preset-env")({
autoprefixer: { autoprefixer: {
flexbox: 'no-2009', flexbox: "no-2009",
}, },
stage: 3, stage: 3,
}), }),
...@@ -45,10 +45,10 @@ module.exports = { ...@@ -45,10 +45,10 @@ module.exports = {
}, },
}, },
{ {
loader: 'sass-loader', loader: "sass-loader",
}, },
], ],
}) })
return config; return config
}, },
}; }
import './style.scss' import "./style.scss"
import '../src/assets/font/iconfont.css' import "../src/assets/font/iconfont.css"
import '@csstools/normalize.css/normalize.css' import "@csstools/normalize.css/normalize.css"
\ No newline at end of file
...@@ -22,4 +22,4 @@ ...@@ -22,4 +22,4 @@
width: 375px; width: 375px;
height: 667px; height: 667px;
background: #fff; background: #fff;
} }
\ No newline at end of file
新m端前端项目 新 m 端前端项目
\ No newline at end of file
'use strict'; "use strict"
const fs = require('fs'); const fs = require("fs")
const path = require('path'); const path = require("path")
const paths = require('./paths'); const paths = require("./paths")
// Make sure that including paths.js after env.js will read .env variables. // Make sure that including paths.js after env.js will read .env variables.
delete require.cache[require.resolve('./paths')]; delete require.cache[require.resolve("./paths")]
const NODE_ENV = process.env.NODE_ENV; const NODE_ENV = process.env.NODE_ENV
if (!NODE_ENV) { if (!NODE_ENV) {
throw new Error( throw new Error(
'The NODE_ENV environment variable is required but was not specified.' "The NODE_ENV environment variable is required but was not specified."
); )
} }
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
...@@ -21,24 +21,24 @@ const dotenvFiles = [ ...@@ -21,24 +21,24 @@ const dotenvFiles = [
// Don't include `.env.local` for `test` environment // Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same // since normally you expect tests to produce the same
// results for everyone // results for everyone
NODE_ENV !== 'test' && `${paths.dotenv}.local`, NODE_ENV !== "test" && `${paths.dotenv}.local`,
paths.dotenv, paths.dotenv,
].filter(Boolean); ].filter(Boolean)
// Load environment variables from .env* files. Suppress warnings using silent // Load environment variables from .env* files. Suppress warnings using silent
// if this file is missing. dotenv will never modify any environment variables // if this file is missing. dotenv will never modify any environment variables
// that have already been set. Variable expansion is supported in .env files. // that have already been set. Variable expansion is supported in .env files.
// https://github.com/motdotla/dotenv // https://github.com/motdotla/dotenv
// https://github.com/motdotla/dotenv-expand // https://github.com/motdotla/dotenv-expand
dotenvFiles.forEach(dotenvFile => { dotenvFiles.forEach((dotenvFile) => {
if (fs.existsSync(dotenvFile)) { if (fs.existsSync(dotenvFile)) {
require('dotenv-expand')( require("dotenv-expand")(
require('dotenv').config({ require("dotenv").config({
path: dotenvFile, path: dotenvFile,
}) })
); )
} }
}); })
// We support resolving modules according to `NODE_PATH`. // We support resolving modules according to `NODE_PATH`.
// This lets you use absolute paths in imports inside large monorepos: // This lets you use absolute paths in imports inside large monorepos:
...@@ -49,29 +49,29 @@ dotenvFiles.forEach(dotenvFile => { ...@@ -49,29 +49,29 @@ dotenvFiles.forEach(dotenvFile => {
// Otherwise, we risk importing Node.js core modules into an app instead of webpack shims. // Otherwise, we risk importing Node.js core modules into an app instead of webpack shims.
// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421 // https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
// We also resolve them to make sure all tools using them work consistently. // We also resolve them to make sure all tools using them work consistently.
const appDirectory = fs.realpathSync(process.cwd()); const appDirectory = fs.realpathSync(process.cwd())
process.env.NODE_PATH = (process.env.NODE_PATH || '') process.env.NODE_PATH = (process.env.NODE_PATH || "")
.split(path.delimiter) .split(path.delimiter)
.filter(folder => folder && !path.isAbsolute(folder)) .filter((folder) => folder && !path.isAbsolute(folder))
.map(folder => path.resolve(appDirectory, folder)) .map((folder) => path.resolve(appDirectory, folder))
.join(path.delimiter); .join(path.delimiter)
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
// injected into the application via DefinePlugin in webpack configuration. // injected into the application via DefinePlugin in webpack configuration.
const REACT_APP = /^REACT_APP_/i; const REACT_APP = /^REACT_APP_/i
function getClientEnvironment(publicUrl) { function getClientEnvironment(publicUrl) {
const raw = Object.keys(process.env) const raw = Object.keys(process.env)
.filter(key => REACT_APP.test(key)) .filter((key) => REACT_APP.test(key))
.reduce( .reduce(
(env, key) => { (env, key) => {
env[key] = process.env[key]; env[key] = process.env[key]
return env; return env
}, },
{ {
// Useful for determining whether we’re running in production mode. // Useful for determining whether we’re running in production mode.
// Most importantly, it switches React into the correct mode. // Most importantly, it switches React into the correct mode.
NODE_ENV: process.env.NODE_ENV || 'development', NODE_ENV: process.env.NODE_ENV || "development",
// Useful for resolving the correct path to static assets in `public`. // Useful for resolving the correct path to static assets in `public`.
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />. // For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
// This should only be used as an escape hatch. Normally you would put // This should only be used as an escape hatch. Normally you would put
...@@ -86,16 +86,16 @@ function getClientEnvironment(publicUrl) { ...@@ -86,16 +86,16 @@ function getClientEnvironment(publicUrl) {
WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH, WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT, WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
} }
); )
// Stringify all values so we can feed into webpack DefinePlugin // Stringify all values so we can feed into webpack DefinePlugin
const stringified = { const stringified = {
'process.env': Object.keys(raw).reduce((env, key) => { "process.env": Object.keys(raw).reduce((env, key) => {
env[key] = JSON.stringify(raw[key]); env[key] = JSON.stringify(raw[key])
return env; return env
}, {}), }, {}),
}; }
return { raw, stringified }; return { raw, stringified }
} }
module.exports = getClientEnvironment; module.exports = getClientEnvironment
'use strict'; "use strict"
const fs = require('fs'); const fs = require("fs")
const path = require('path'); const path = require("path")
const crypto = require('crypto'); const crypto = require("crypto")
const chalk = require('react-dev-utils/chalk'); const chalk = require("react-dev-utils/chalk")
const paths = require('./paths'); const paths = require("./paths")
// Ensure the certificate and key provided are valid and if not // Ensure the certificate and key provided are valid and if not
// throw an easy to debug error // throw an easy to debug error
function validateKeyAndCerts({ cert, key, keyFile, crtFile }) { function validateKeyAndCerts({ cert, key, keyFile, crtFile }) {
let encrypted; let encrypted
try { try {
// publicEncrypt will throw an error with an invalid cert // publicEncrypt will throw an error with an invalid cert
encrypted = crypto.publicEncrypt(cert, Buffer.from('test')); encrypted = crypto.publicEncrypt(cert, Buffer.from("test"))
} catch (err) { } catch (err) {
throw new Error( throw new Error(
`The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}` `The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}`
); )
} }
try { try {
// privateDecrypt will throw an error with an invalid key // privateDecrypt will throw an error with an invalid key
crypto.privateDecrypt(key, encrypted); crypto.privateDecrypt(key, encrypted)
} catch (err) { } catch (err) {
throw new Error( throw new Error(
`The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${ `The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${
err.message err.message
}` }`
); )
} }
} }
...@@ -38,29 +38,29 @@ function readEnvFile(file, type) { ...@@ -38,29 +38,29 @@ function readEnvFile(file, type) {
`You specified ${chalk.cyan( `You specified ${chalk.cyan(
type type
)} in your env, but the file "${chalk.yellow(file)}" can't be found.` )} in your env, but the file "${chalk.yellow(file)}" can't be found.`
); )
} }
return fs.readFileSync(file); return fs.readFileSync(file)
} }
// Get the https config // Get the https config
// Return cert files if provided in env, otherwise just true or false // Return cert files if provided in env, otherwise just true or false
function getHttpsConfig() { function getHttpsConfig() {
const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env; const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env
const isHttps = HTTPS === 'true'; const isHttps = HTTPS === "true"
if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) { if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) {
const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE); const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE)
const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE); const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE)
const config = { const config = {
cert: readEnvFile(crtFile, 'SSL_CRT_FILE'), cert: readEnvFile(crtFile, "SSL_CRT_FILE"),
key: readEnvFile(keyFile, 'SSL_KEY_FILE'), key: readEnvFile(keyFile, "SSL_KEY_FILE"),
}; }
validateKeyAndCerts({ ...config, keyFile, crtFile }); validateKeyAndCerts({ ...config, keyFile, crtFile })
return config; return config
} }
return isHttps; return isHttps
} }
module.exports = getHttpsConfig; module.exports = getHttpsConfig
'use strict'; "use strict"
// This is a custom Jest transformer turning style imports into empty objects. // This is a custom Jest transformer turning style imports into empty objects.
// http://facebook.github.io/jest/docs/en/webpack.html // http://facebook.github.io/jest/docs/en/webpack.html
module.exports = { module.exports = {
process() { process() {
return 'module.exports = {};'; return "module.exports = {};"
}, },
getCacheKey() { getCacheKey() {
// The output is always the same. // The output is always the same.
return 'cssTransform'; return "cssTransform"
}, },
}; }
'use strict'; "use strict"
const path = require('path'); const path = require("path")
const camelcase = require('camelcase'); const camelcase = require("camelcase")
// This is a custom Jest transformer turning file imports into filenames. // This is a custom Jest transformer turning file imports into filenames.
// http://facebook.github.io/jest/docs/en/webpack.html // http://facebook.github.io/jest/docs/en/webpack.html
module.exports = { module.exports = {
process(src, filename) { process(src, filename) {
const assetFilename = JSON.stringify(path.basename(filename)); const assetFilename = JSON.stringify(path.basename(filename))
if (filename.match(/\.svg$/)) { if (filename.match(/\.svg$/)) {
// Based on how SVGR generates a component name: // Based on how SVGR generates a component name:
// https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6 // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
const pascalCaseFilename = camelcase(path.parse(filename).name, { const pascalCaseFilename = camelcase(path.parse(filename).name, {
pascalCase: true, pascalCase: true,
}); })
const componentName = `Svg${pascalCaseFilename}`; const componentName = `Svg${pascalCaseFilename}`
return `const React = require('react'); return `const React = require('react');
module.exports = { module.exports = {
__esModule: true, __esModule: true,
...@@ -32,9 +32,9 @@ module.exports = { ...@@ -32,9 +32,9 @@ module.exports = {
}) })
}; };
}), }),
};`; };`
} }
return `module.exports = ${assetFilename};`; return `module.exports = ${assetFilename};`
}, },
}; }
'use strict'; "use strict"
const fs = require('fs'); const fs = require("fs")
const path = require('path'); const path = require("path")
const paths = require('./paths'); const paths = require("./paths")
const chalk = require('react-dev-utils/chalk'); const chalk = require("react-dev-utils/chalk")
const resolve = require('resolve'); const resolve = require("resolve")
/** /**
* Get additional module paths based on the baseUrl of a compilerOptions object. * Get additional module paths based on the baseUrl of a compilerOptions object.
...@@ -12,7 +12,7 @@ const resolve = require('resolve'); ...@@ -12,7 +12,7 @@ const resolve = require('resolve');
* @param {Object} options * @param {Object} options
*/ */
function getAdditionalModulePaths(options = {}) { function getAdditionalModulePaths(options = {}) {
const baseUrl = options.baseUrl; const baseUrl = options.baseUrl
// We need to explicitly check for null and undefined (and not a falsy value) because // We need to explicitly check for null and undefined (and not a falsy value) because
// TypeScript treats an empty string as `.`. // TypeScript treats an empty string as `.`.
...@@ -21,21 +21,21 @@ function getAdditionalModulePaths(options = {}) { ...@@ -21,21 +21,21 @@ function getAdditionalModulePaths(options = {}) {
// Note that NODE_PATH is deprecated and will be removed // Note that NODE_PATH is deprecated and will be removed
// in the next major release of create-react-app. // in the next major release of create-react-app.
const nodePath = process.env.NODE_PATH || ''; const nodePath = process.env.NODE_PATH || ""
return nodePath.split(path.delimiter).filter(Boolean); return nodePath.split(path.delimiter).filter(Boolean)
} }
const baseUrlResolved = path.resolve(paths.appPath, baseUrl); const baseUrlResolved = path.resolve(paths.appPath, baseUrl)
// We don't need to do anything if `baseUrl` is set to `node_modules`. This is // We don't need to do anything if `baseUrl` is set to `node_modules`. This is
// the default behavior. // the default behavior.
if (path.relative(paths.appNodeModules, baseUrlResolved) === '') { if (path.relative(paths.appNodeModules, baseUrlResolved) === "") {
return null; return null
} }
// Allow the user set the `baseUrl` to `appSrc`. // Allow the user set the `baseUrl` to `appSrc`.
if (path.relative(paths.appSrc, baseUrlResolved) === '') { if (path.relative(paths.appSrc, baseUrlResolved) === "") {
return [paths.appSrc]; return [paths.appSrc]
} }
// If the path is equal to the root directory we ignore it here. // If the path is equal to the root directory we ignore it here.
...@@ -43,17 +43,17 @@ function getAdditionalModulePaths(options = {}) { ...@@ -43,17 +43,17 @@ function getAdditionalModulePaths(options = {}) {
// not transpiled outside of `src`. We do allow importing them with the // not transpiled outside of `src`. We do allow importing them with the
// absolute path (e.g. `src/Components/Button.js`) but we set that up with // absolute path (e.g. `src/Components/Button.js`) but we set that up with
// an alias. // an alias.
if (path.relative(paths.appPath, baseUrlResolved) === '') { if (path.relative(paths.appPath, baseUrlResolved) === "") {
return null; return null
} }
// Otherwise, throw an error. // Otherwise, throw an error.
throw new Error( throw new Error(
chalk.red.bold( chalk.red.bold(
"Your project's `baseUrl` can only be set to `src` or `node_modules`." + "Your project's `baseUrl` can only be set to `src` or `node_modules`." +
' Create React App does not support other values at this time.' " Create React App does not support other values at this time."
) )
); )
} }
/** /**
...@@ -62,18 +62,18 @@ function getAdditionalModulePaths(options = {}) { ...@@ -62,18 +62,18 @@ function getAdditionalModulePaths(options = {}) {
* @param {*} options * @param {*} options
*/ */
function getWebpackAliases(options = {}) { function getWebpackAliases(options = {}) {
const baseUrl = options.baseUrl; const baseUrl = options.baseUrl
if (!baseUrl) { if (!baseUrl) {
return {}; return {}
} }
const baseUrlResolved = path.resolve(paths.appPath, baseUrl); const baseUrlResolved = path.resolve(paths.appPath, baseUrl)
if (path.relative(paths.appPath, baseUrlResolved) === '') { if (path.relative(paths.appPath, baseUrlResolved) === "") {
return { return {
src: paths.appSrc, src: paths.appSrc,
}; }
} }
} }
...@@ -83,59 +83,59 @@ function getWebpackAliases(options = {}) { ...@@ -83,59 +83,59 @@ function getWebpackAliases(options = {}) {
* @param {*} options * @param {*} options
*/ */
function getJestAliases(options = {}) { function getJestAliases(options = {}) {
const baseUrl = options.baseUrl; const baseUrl = options.baseUrl
if (!baseUrl) { if (!baseUrl) {
return {}; return {}
} }
const baseUrlResolved = path.resolve(paths.appPath, baseUrl); const baseUrlResolved = path.resolve(paths.appPath, baseUrl)
if (path.relative(paths.appPath, baseUrlResolved) === '') { if (path.relative(paths.appPath, baseUrlResolved) === "") {
return { return {
'^src/(.*)$': '<rootDir>/src/$1', "^src/(.*)$": "<rootDir>/src/$1",
}; }
} }
} }
function getModules() { function getModules() {
// Check if TypeScript is setup // Check if TypeScript is setup
const hasTsConfig = fs.existsSync(paths.appTsConfig); const hasTsConfig = fs.existsSync(paths.appTsConfig)
const hasJsConfig = fs.existsSync(paths.appJsConfig); const hasJsConfig = fs.existsSync(paths.appJsConfig)
if (hasTsConfig && hasJsConfig) { if (hasTsConfig && hasJsConfig) {
throw new Error( throw new Error(
'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.' "You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file."
); )
} }
let config; let config
// If there's a tsconfig.json we assume it's a // If there's a tsconfig.json we assume it's a
// TypeScript project and set up the config // TypeScript project and set up the config
// based on tsconfig.json // based on tsconfig.json
if (hasTsConfig) { if (hasTsConfig) {
const ts = require(resolve.sync('typescript', { const ts = require(resolve.sync("typescript", {
basedir: paths.appNodeModules, basedir: paths.appNodeModules,
})); }))
config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config; config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config
// Otherwise we'll check if there is jsconfig.json // Otherwise we'll check if there is jsconfig.json
// for non TS projects. // for non TS projects.
} else if (hasJsConfig) { } else if (hasJsConfig) {
config = require(paths.appJsConfig); config = require(paths.appJsConfig)
} }
config = config || {}; config = config || {}
const options = config.compilerOptions || {}; const options = config.compilerOptions || {}
const additionalModulePaths = getAdditionalModulePaths(options); const additionalModulePaths = getAdditionalModulePaths(options)
return { return {
additionalModulePaths: additionalModulePaths, additionalModulePaths: additionalModulePaths,
webpackAliases: getWebpackAliases(options), webpackAliases: getWebpackAliases(options),
jestAliases: getJestAliases(options), jestAliases: getJestAliases(options),
hasTsConfig, hasTsConfig,
}; }
} }
module.exports = getModules(); module.exports = getModules()
'use strict'; "use strict"
const path = require('path'); const path = require("path")
const fs = require('fs'); const fs = require("fs")
const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath'); const getPublicUrlOrPath = require("react-dev-utils/getPublicUrlOrPath")
// Make sure any symlinks in the project folder are resolved: // Make sure any symlinks in the project folder are resolved:
// https://github.com/facebook/create-react-app/issues/637 // https://github.com/facebook/create-react-app/issues/637
const appDirectory = fs.realpathSync(process.cwd()); const appDirectory = fs.realpathSync(process.cwd())
const resolveApp = relativePath => path.resolve(appDirectory, relativePath); const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath)
// We use `PUBLIC_URL` environment variable or "homepage" field to infer // We use `PUBLIC_URL` environment variable or "homepage" field to infer
// "public path" at which the app is served. // "public path" at which the app is served.
...@@ -16,57 +16,55 @@ const resolveApp = relativePath => path.resolve(appDirectory, relativePath); ...@@ -16,57 +16,55 @@ const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
// We can't use a relative path in HTML because we don't want to load something // We can't use a relative path in HTML because we don't want to load something
// like /todos/42/static/js/bundle.7289d.js. We have to know the root. // like /todos/42/static/js/bundle.7289d.js. We have to know the root.
const publicUrlOrPath = getPublicUrlOrPath( const publicUrlOrPath = getPublicUrlOrPath(
process.env.NODE_ENV === 'development', process.env.NODE_ENV === "development",
require(resolveApp('package.json')).homepage, require(resolveApp("package.json")).homepage,
process.env.PUBLIC_URL process.env.PUBLIC_URL
); )
const moduleFileExtensions = [ const moduleFileExtensions = [
'web.mjs', "web.mjs",
'mjs', "mjs",
'web.js', "web.js",
'js', "js",
'web.ts', "web.ts",
'ts', "ts",
'web.tsx', "web.tsx",
'tsx', "tsx",
'json', "json",
'web.jsx', "web.jsx",
'jsx', "jsx",
]; ]
// Resolve file paths in the same order as webpack // Resolve file paths in the same order as webpack
const resolveModule = (resolveFn, filePath) => { const resolveModule = (resolveFn, filePath) => {
const extension = moduleFileExtensions.find(extension => const extension = moduleFileExtensions.find((extension) =>
fs.existsSync(resolveFn(`${filePath}.${extension}`)) fs.existsSync(resolveFn(`${filePath}.${extension}`))
); )
if (extension) { if (extension) {
return resolveFn(`${filePath}.${extension}`); return resolveFn(`${filePath}.${extension}`)
} }
return resolveFn(`${filePath}.js`); return resolveFn(`${filePath}.js`)
}; }
// config after eject: we're in ./config/ // config after eject: we're in ./config/
module.exports = { module.exports = {
dotenv: resolveApp('.env'), dotenv: resolveApp(".env"),
appPath: resolveApp('.'), appPath: resolveApp("."),
appBuild: resolveApp('build'), appBuild: resolveApp("build"),
appPublic: resolveApp('public'), appPublic: resolveApp("public"),
appHtml: resolveApp('public/index.html'), appHtml: resolveApp("public/index.html"),
appIndexJs: resolveModule(resolveApp, 'src/index'), appIndexJs: resolveModule(resolveApp, "src/index"),
appPackageJson: resolveApp('package.json'), appPackageJson: resolveApp("package.json"),
appSrc: resolveApp('src'), appSrc: resolveApp("src"),
appTsConfig: resolveApp('tsconfig.json'), appTsConfig: resolveApp("tsconfig.json"),
appJsConfig: resolveApp('jsconfig.json'), appJsConfig: resolveApp("jsconfig.json"),
yarnLockFile: resolveApp('yarn.lock'), yarnLockFile: resolveApp("yarn.lock"),
testsSetup: resolveModule(resolveApp, 'src/setupTests'), testsSetup: resolveModule(resolveApp, "src/setupTests"),
proxySetup: resolveApp('src/setupProxy.js'), proxySetup: resolveApp("src/setupProxy.js"),
appNodeModules: resolveApp('node_modules'), appNodeModules: resolveApp("node_modules"),
publicUrlOrPath, publicUrlOrPath,
}; }
module.exports.moduleFileExtensions = moduleFileExtensions
module.exports.moduleFileExtensions = moduleFileExtensions;
'use strict'; "use strict"
const { resolveModuleName } = require('ts-pnp'); const { resolveModuleName } = require("ts-pnp")
exports.resolveModuleName = ( exports.resolveModuleName = (
typescript, typescript,
...@@ -15,8 +15,8 @@ exports.resolveModuleName = ( ...@@ -15,8 +15,8 @@ exports.resolveModuleName = (
compilerOptions, compilerOptions,
resolutionHost, resolutionHost,
typescript.resolveModuleName typescript.resolveModuleName
); )
}; }
exports.resolveTypeReferenceDirective = ( exports.resolveTypeReferenceDirective = (
typescript, typescript,
...@@ -31,5 +31,5 @@ exports.resolveTypeReferenceDirective = ( ...@@ -31,5 +31,5 @@ exports.resolveTypeReferenceDirective = (
compilerOptions, compilerOptions,
resolutionHost, resolutionHost,
typescript.resolveTypeReferenceDirective typescript.resolveTypeReferenceDirective
); )
}; }
'use strict'; "use strict"
const fs = require('fs'); const fs = require("fs")
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware'); const errorOverlayMiddleware = require("react-dev-utils/errorOverlayMiddleware")
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware'); const evalSourceMapMiddleware = require("react-dev-utils/evalSourceMapMiddleware")
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware'); const noopServiceWorkerMiddleware = require("react-dev-utils/noopServiceWorkerMiddleware")
const ignoredFiles = require('react-dev-utils/ignoredFiles'); const ignoredFiles = require("react-dev-utils/ignoredFiles")
const redirectServedPath = require('react-dev-utils/redirectServedPathMiddleware'); const redirectServedPath = require("react-dev-utils/redirectServedPathMiddleware")
const paths = require('./paths'); const paths = require("./paths")
const getHttpsConfig = require('./getHttpsConfig'); const getHttpsConfig = require("./getHttpsConfig")
const host = process.env.HOST || '0.0.0.0'; const host = process.env.HOST || "0.0.0.0"
const sockHost = process.env.WDS_SOCKET_HOST; const sockHost = process.env.WDS_SOCKET_HOST
const sockPath = process.env.WDS_SOCKET_PATH; // default: '/sockjs-node' const sockPath = process.env.WDS_SOCKET_PATH // default: '/sockjs-node'
const sockPort = process.env.WDS_SOCKET_PORT; const sockPort = process.env.WDS_SOCKET_PORT
module.exports = function(proxy, allowedHost) { module.exports = function (proxy, allowedHost) {
return { return {
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote // WebpackDevServer 2.4.3 introduced a security fix that prevents remote
// websites from potentially accessing local content through DNS rebinding: // websites from potentially accessing local content through DNS rebinding:
...@@ -33,12 +33,12 @@ module.exports = function(proxy, allowedHost) { ...@@ -33,12 +33,12 @@ module.exports = function(proxy, allowedHost) {
// specified the `proxy` setting. Finally, we let you override it if you // specified the `proxy` setting. Finally, we let you override it if you
// really know what you're doing with a special environment variable. // really know what you're doing with a special environment variable.
disableHostCheck: disableHostCheck:
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true', !proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === "true",
// Enable gzip compression of generated files. // Enable gzip compression of generated files.
compress: true, compress: true,
// Silence WebpackDevServer's own logs since they're generally not useful. // Silence WebpackDevServer's own logs since they're generally not useful.
// It will still show compile warnings and errors with this setting. // It will still show compile warnings and errors with this setting.
clientLogLevel: 'none', clientLogLevel: "none",
// By default WebpackDevServer serves physical files from current directory // By default WebpackDevServer serves physical files from current directory
// in addition to all the virtual build products that it serves from memory. // in addition to all the virtual build products that it serves from memory.
// This is confusing because those files won’t automatically be available in // This is confusing because those files won’t automatically be available in
...@@ -65,7 +65,7 @@ module.exports = function(proxy, allowedHost) { ...@@ -65,7 +65,7 @@ module.exports = function(proxy, allowedHost) {
hot: true, hot: true,
// Use 'ws' instead of 'sockjs-node' on server since we're using native // Use 'ws' instead of 'sockjs-node' on server since we're using native
// websockets in `webpackHotDevClient`. // websockets in `webpackHotDevClient`.
transportMode: 'ws', transportMode: "ws",
// Prevent a WS client from getting injected as we're already including // Prevent a WS client from getting injected as we're already including
// `webpackHotDevClient`. // `webpackHotDevClient`.
injectClient: false, injectClient: false,
...@@ -106,25 +106,25 @@ module.exports = function(proxy, allowedHost) { ...@@ -106,25 +106,25 @@ module.exports = function(proxy, allowedHost) {
// Keep `evalSourceMapMiddleware` and `errorOverlayMiddleware` // Keep `evalSourceMapMiddleware` and `errorOverlayMiddleware`
// middlewares before `redirectServedPath` otherwise will not have any effect // middlewares before `redirectServedPath` otherwise will not have any effect
// This lets us fetch source contents from webpack for the error overlay // This lets us fetch source contents from webpack for the error overlay
app.use(evalSourceMapMiddleware(server)); app.use(evalSourceMapMiddleware(server))
// This lets us open files from the runtime error overlay. // This lets us open files from the runtime error overlay.
app.use(errorOverlayMiddleware()); app.use(errorOverlayMiddleware())
if (fs.existsSync(paths.proxySetup)) { if (fs.existsSync(paths.proxySetup)) {
// This registers user provided middleware for proxy reasons // This registers user provided middleware for proxy reasons
require(paths.proxySetup)(app); require(paths.proxySetup)(app)
} }
}, },
after(app) { after(app) {
// Redirect to `PUBLIC_URL` or `homepage` from `package.json` if url not match // Redirect to `PUBLIC_URL` or `homepage` from `package.json` if url not match
app.use(redirectServedPath(paths.publicUrlOrPath)); app.use(redirectServedPath(paths.publicUrlOrPath))
// This service worker file is effectively a 'no-op' that will reset any // This service worker file is effectively a 'no-op' that will reset any
// previous service worker registered for the same host:port combination. // previous service worker registered for the same host:port combination.
// We do this in development to avoid hitting the production cache if // We do this in development to avoid hitting the production cache if
// it used the same host and port. // it used the same host and port.
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432 // https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
app.use(noopServiceWorkerMiddleware(paths.publicUrlOrPath)); app.use(noopServiceWorkerMiddleware(paths.publicUrlOrPath))
}, },
}; }
}; }
'use strict'; "use strict"
// Do this as the first thing so that any code reading it knows the right env. // Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'development'; process.env.BABEL_ENV = "development"
process.env.NODE_ENV = 'development'; process.env.NODE_ENV = "development"
process.env.REACT_APP_BUILD_ENV = 'development'; process.env.REACT_APP_BUILD_ENV = "development"
// Makes the script crash on unhandled rejections instead of silently // Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will // ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code. // terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => { process.on("unhandledRejection", (err) => {
throw err; throw err
}); })
// Ensure environment variables are read. // Ensure environment variables are read.
require('../config/env'); require("../config/env")
const fs = require("fs")
const fs = require('fs'); const chalk = require("react-dev-utils/chalk")
const chalk = require('react-dev-utils/chalk'); const webpack = require("webpack")
const webpack = require('webpack'); const WebpackDevServer = require("webpack-dev-server")
const WebpackDevServer = require('webpack-dev-server'); const clearConsole = require("react-dev-utils/clearConsole")
const clearConsole = require('react-dev-utils/clearConsole'); const checkRequiredFiles = require("react-dev-utils/checkRequiredFiles")
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const { const {
choosePort, choosePort,
createCompiler, createCompiler,
prepareProxy, prepareProxy,
prepareUrls, prepareUrls,
} = require('react-dev-utils/WebpackDevServerUtils'); } = require("react-dev-utils/WebpackDevServerUtils")
const openBrowser = require('react-dev-utils/openBrowser'); const openBrowser = require("react-dev-utils/openBrowser")
const paths = require('../config/paths'); const paths = require("../config/paths")
const configFactory = require('../config/webpack.config'); const configFactory = require("../config/webpack.config")
const createDevServerConfig = require('../config/webpackDevServer.config'); const createDevServerConfig = require("../config/webpackDevServer.config")
const useYarn = fs.existsSync(paths.yarnLockFile); const useYarn = fs.existsSync(paths.yarnLockFile)
const isInteractive = process.stdout.isTTY; const isInteractive = process.stdout.isTTY
// Warn and crash if required files are missing // Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1); process.exit(1)
} }
// Tools like Cloud9 rely on this. // Tools like Cloud9 rely on this.
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000
const HOST = process.env.HOST || '0.0.0.0'; const HOST = process.env.HOST || "0.0.0.0"
if (process.env.HOST) { if (process.env.HOST) {
console.log( console.log(
...@@ -52,41 +51,41 @@ if (process.env.HOST) { ...@@ -52,41 +51,41 @@ if (process.env.HOST) {
chalk.bold(process.env.HOST) chalk.bold(process.env.HOST)
)}` )}`
) )
); )
console.log( console.log(
`If this was unintentional, check that you haven't mistakenly set it in your shell.` `If this was unintentional, check that you haven't mistakenly set it in your shell.`
); )
console.log( console.log(
`Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}` `Learn more here: ${chalk.yellow("https://bit.ly/CRA-advanced-config")}`
); )
console.log(); console.log()
} }
// We require that you explictly set browsers and do not fall back to // We require that you explictly set browsers and do not fall back to
// browserslist defaults. // browserslist defaults.
const { checkBrowsers } = require('react-dev-utils/browsersHelper'); const { checkBrowsers } = require("react-dev-utils/browsersHelper")
checkBrowsers(paths.appPath, isInteractive) checkBrowsers(paths.appPath, isInteractive)
.then(() => { .then(() => {
// We attempt to use the default port but if it is busy, we offer the user to // We attempt to use the default port but if it is busy, we offer the user to
// run on a different port. `choosePort()` Promise resolves to the next free port. // run on a different port. `choosePort()` Promise resolves to the next free port.
return choosePort(HOST, DEFAULT_PORT); return choosePort(HOST, DEFAULT_PORT)
}) })
.then(port => { .then((port) => {
if (port == null) { if (port == null) {
// We have not found a port. // We have not found a port.
return; return
} }
const config = configFactory('development'); const config = configFactory("development")
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; const protocol = process.env.HTTPS === "true" ? "https" : "http"
const appName = require(paths.appPackageJson).name; const appName = require(paths.appPackageJson).name
const useTypeScript = fs.existsSync(paths.appTsConfig); const useTypeScript = fs.existsSync(paths.appTsConfig)
const urls = prepareUrls(protocol, HOST, port); const urls = prepareUrls(protocol, HOST, port)
const devSocket = { const devSocket = {
warnings: warnings => warnings: (warnings) =>
devServer.sockWrite(devServer.sockets, 'warnings', warnings), devServer.sockWrite(devServer.sockets, "warnings", warnings),
errors: errors => errors: (errors) =>
devServer.sockWrite(devServer.sockets, 'errors', errors), devServer.sockWrite(devServer.sockets, "errors", errors),
}; }
// Create a webpack compiler that is configured with custom messages. // Create a webpack compiler that is configured with custom messages.
const compiler = createCompiler({ const compiler = createCompiler({
appName, appName,
...@@ -96,38 +95,38 @@ checkBrowsers(paths.appPath, isInteractive) ...@@ -96,38 +95,38 @@ checkBrowsers(paths.appPath, isInteractive)
useYarn, useYarn,
useTypeScript, useTypeScript,
webpack, webpack,
}); })
// Load proxy config // Load proxy config
const proxySetting = require(paths.appPackageJson).proxy; const proxySetting = require(paths.appPackageJson).proxy
const proxyConfig = prepareProxy(proxySetting, paths.appPublic); const proxyConfig = prepareProxy(proxySetting, paths.appPublic)
// Serve webpack assets generated by the compiler over a web server. // Serve webpack assets generated by the compiler over a web server.
const serverConfig = createDevServerConfig( const serverConfig = createDevServerConfig(
proxyConfig, proxyConfig,
urls.lanUrlForConfig urls.lanUrlForConfig
); )
const devServer = new WebpackDevServer(compiler, serverConfig); const devServer = new WebpackDevServer(compiler, serverConfig)
// Launch WebpackDevServer. // Launch WebpackDevServer.
devServer.listen(port, HOST, err => { devServer.listen(port, HOST, (err) => {
if (err) { if (err) {
return console.log(err); return console.log(err)
} }
if (isInteractive) { if (isInteractive) {
clearConsole(); clearConsole()
} }
console.log(chalk.cyan('Starting the development server...\n')); console.log(chalk.cyan("Starting the development server...\n"))
openBrowser(urls.localUrlForBrowser); openBrowser(urls.localUrlForBrowser)
}); })
['SIGINT', 'SIGTERM'].forEach(function(sig) { ;["SIGINT", "SIGTERM"].forEach(function (sig) {
process.on(sig, function() { process.on(sig, function () {
devServer.close(); devServer.close()
process.exit(); process.exit()
}); })
}); })
}) })
.catch(err => { .catch((err) => {
if (err && err.message) { if (err && err.message) {
console.log(err.message); console.log(err.message)
} }
process.exit(1); process.exit(1)
}); })
'use strict'; "use strict"
// Do this as the first thing so that any code reading it knows the right env. // Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'test'; process.env.BABEL_ENV = "test"
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = "test"
process.env.PUBLIC_URL = ''; process.env.PUBLIC_URL = ""
// Makes the script crash on unhandled rejections instead of silently // Makes the script crash on unhandled rejections instead of silently
// ignoring them. In the future, promise rejections that are not handled will // ignoring them. In the future, promise rejections that are not handled will
// terminate the Node.js process with a non-zero exit code. // terminate the Node.js process with a non-zero exit code.
process.on('unhandledRejection', err => { process.on("unhandledRejection", (err) => {
throw err; throw err
}); })
// Ensure environment variables are read. // Ensure environment variables are read.
require('../config/env'); require("../config/env")
const jest = require("jest")
const jest = require('jest'); const execSync = require("child_process").execSync
const execSync = require('child_process').execSync; let argv = process.argv.slice(2)
let argv = process.argv.slice(2);
function isInGitRepository() { function isInGitRepository() {
try { try {
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' }); execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore" })
return true; return true
} catch (e) { } catch (e) {
return false; return false
} }
} }
function isInMercurialRepository() { function isInMercurialRepository() {
try { try {
execSync('hg --cwd . root', { stdio: 'ignore' }); execSync("hg --cwd . root", { stdio: "ignore" })
return true; return true
} catch (e) { } catch (e) {
return false; return false
} }
} }
...@@ -42,19 +41,18 @@ function isInMercurialRepository() { ...@@ -42,19 +41,18 @@ function isInMercurialRepository() {
// or explicitly running all tests // or explicitly running all tests
if ( if (
!process.env.CI && !process.env.CI &&
argv.indexOf('--coverage') === -1 && argv.indexOf("--coverage") === -1 &&
argv.indexOf('--no-watch') === -1 && argv.indexOf("--no-watch") === -1 &&
argv.indexOf('--watchAll') === -1 argv.indexOf("--watchAll") === -1
) { ) {
// https://github.com/facebook/create-react-app/issues/5210 // https://github.com/facebook/create-react-app/issues/5210
const hasSourceControl = isInGitRepository() || isInMercurialRepository(); const hasSourceControl = isInGitRepository() || isInMercurialRepository()
argv.push(hasSourceControl ? '--watch' : '--watchAll'); argv.push(hasSourceControl ? "--watch" : "--watchAll")
} }
// Jest doesn't have this option so we'll remove it // Jest doesn't have this option so we'll remove it
if (argv.indexOf('--no-watch') !== -1) { if (argv.indexOf("--no-watch") !== -1) {
argv = argv.filter(arg => arg !== '--no-watch'); argv = argv.filter((arg) => arg !== "--no-watch")
} }
jest.run(argv)
jest.run(argv);
import React, { PureComponent } from 'react'; import React, { PureComponent } from "react"
function setSize() { function setSize() {
document.body.style.height = `${window.innerHeight}px` document.body.style.height = `${window.innerHeight}px`
document.getElementById('root').style.height = `${window.innerHeight}px` document.getElementById("root").style.height = `${window.innerHeight}px`
document.documentElement.style.height = `${window.innerHeight}px` document.documentElement.style.height = `${window.innerHeight}px`
} }
export default function (WrappedComponent) { export default function (WrappedComponent) {
return class extends PureComponent { return class extends PureComponent {
componentDidMount() { componentDidMount() {
setSize() setSize()
window.addEventListener('resize', setSize) window.addEventListener("resize", setSize)
} }
componentWillUnmount() { componentWillUnmount() {
document.body.style.height = `auto` document.body.style.height = `auto`
document.getElementById('root').style.height = `auto` document.getElementById("root").style.height = `auto`
document.documentElement.style.height = 'auto' document.documentElement.style.height = "auto"
window.removeEventListener('resize', setSize) window.removeEventListener("resize", setSize)
} }
render() { render() {
return ( return <WrappedComponent {...this.props} />
<WrappedComponent {...this.props}/>
);
}
} }
}
} }
import React, { PureComponent } from 'react' import React, { PureComponent } from "react"
import NavBar from 'src/common/NavBar' import NavBar from "src/common/NavBar"
export default function WithTab(WrappedComponent) { export default function WithTab(WrappedComponent) {
return class extends PureComponent { return class extends PureComponent {
render() { render() {
return ( return (
<> <>
<WrappedComponent {...this.props}/> <WrappedComponent {...this.props} />
<NavBar/> <NavBar />
</> </>
) )
}
} }
} }
\ No newline at end of file }
export {default as WithTab} from './WithTab' export { default as WithTab } from "./WithTab"
export {default as WithFullSize} from './WithFullSize' export { default as WithFullSize } from "./WithFullSize"
\ No newline at end of file
...@@ -11,7 +11,7 @@ $main-color: #09f; // 主体颜色 ...@@ -11,7 +11,7 @@ $main-color: #09f; // 主体颜色
// 字体 // 字体
$font-family-zh: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", $font-family-zh: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
"Microsoft YaHei", "微软雅黑", Arial, sans-serif; "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
$font-family-en: Arial, sans-serif !default; $font-family-en: Arial, sans-serif !default;
// 盒子模型 // 盒子模型
...@@ -131,7 +131,7 @@ body { ...@@ -131,7 +131,7 @@ body {
padding-bottom: constant(safe-area-inset-bottom); padding-bottom: constant(safe-area-inset-bottom);
} }
img{ img {
max-width: 100%; max-width: 100%;
} }
...@@ -328,13 +328,13 @@ input[type="radio"]:checked:before { ...@@ -328,13 +328,13 @@ input[type="radio"]:checked:before {
height: 48px; height: 48px;
box-shadow: 0 0 3px #9a9a9a; box-shadow: 0 0 3px #9a9a9a;
.label, .track, .button { .label,
.track,
.button {
height: 48px; height: 48px;
} }
} }
} }
} }
.close { .close {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
// 行内元素也可以使用 flex 布局 // 行内元素也可以使用 flex 布局
// .box-example { display: inline-flex; } // .box-example { display: inline-flex; }
// 以下6个属性设置在容器上 // 以下 6 个属性设置在容器上
// 1. flex-direction --> 子项目排列的方向 // 1. flex-direction --> 子项目排列的方向
// row 默认,水平从左到右 // row 默认,水平从左到右
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
// flex-end 交叉轴的终点对齐 // flex-end 交叉轴的终点对齐
// center 交叉轴的中点对齐 // center 交叉轴的中点对齐
// baseline 子项目的第一行文字的基线对齐 // baseline 子项目的第一行文字的基线对齐
// stretch 默认值,如果子项目未设置高度或设为auto,将占满整个容器的高度 // stretch 默认值,如果子项目未设置高度或设为 auto,将占满整个容器的高度
// 6. align-content 定义了多根轴线的对齐方式,如果子项目只有一根轴线,则该属性无效。 // 6. align-content 定义了多根轴线的对齐方式,如果子项目只有一根轴线,则该属性无效。
// flex-start 与交叉轴的起点对齐。 // flex-start 与交叉轴的起点对齐。
...@@ -41,28 +41,28 @@ ...@@ -41,28 +41,28 @@
// space-around 每根轴线两侧的间隔都相等,所以轴线之间的间隔比轴线与边框的间隔大一倍 // space-around 每根轴线两侧的间隔都相等,所以轴线之间的间隔比轴线与边框的间隔大一倍
// stretch 默认值,轴线占满整个交叉轴 // stretch 默认值,轴线占满整个交叉轴
// 以下6个属性设置在子项目上。 // 以下 6 个属性设置在子项目上。
// 1. order 定义子项目的排列顺序,数值越小,排位越靠前。默认值为0. // 1. order 定义子项目的排列顺序,数值越小,排位越靠前。默认值为 0.
// 2. flex-grow 定义子项目放大比例,默认为0,即如果存在剩余空间,也不放大。 // 2. flex-grow 定义子项目放大比例,默认为 0,即如果存在剩余空间,也不放大。
// 如果所有项目的flex-grow属性都为1,则它们将等分剩余空间(如果有的话)。 // 如果所有项目的 flex-grow 属性都为 1,则它们将等分剩余空间(如果有的话)。
// 如果一个项目的flex-grow属性为2,其他项目都为1,则前者占据的剩余空间将比其他项多一倍 // 如果一个项目的 flex-grow 属性为 2,其他项目都为 1,则前者占据的剩余空间将比其他项多一倍
// 3. flex-shrink 定义了子项目的缩小比例,默认为1,即如果空间不足,该项目将缩小。 // 3. flex-shrink 定义了子项目的缩小比例,默认为 1,即如果空间不足,该项目将缩小。
// 如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小。 // 如果所有项目的 flex-shrink 属性都为 1,当空间不足时,都将等比例缩小。
// 如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。 // 如果一个项目的 flex-shrink 属性为 0,其他项目都为 1,则空间不足时,前者不缩小。
// 4. flex-basis 属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。 // 4. flex-basis 属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。
// 浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。 // 浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为 auto,即项目的本来大小。
// 它可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。 // 它可以设为跟 width 或 height 属性一样的值(比如 350px),则项目将占据固定空间。
// 5. flex 该属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。 // 5. flex 该属性是 flex-grow, flex-shrink 和 flex-basis 的简写,默认值为 0 1 auto。后两个属性可选。
// 该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto) // 该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)
// 建议优先使用这个 flex 属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。 // 建议优先使用这个 flex 属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。
// align-self 允许单个项目有与其他项目不一样的对齐方式,可覆盖 align-items 属性。 // align-self 允许单个项目有与其他项目不一样的对齐方式,可覆盖 align-items 属性。
// 默认值为 auto,表示继承父元素的 align-items 属性,如果没有父元素,则等同于 stretch // 默认值为 auto,表示继承父元素的 align-items 属性,如果没有父元素,则等同于 stretch
// 该属性可能取6个值,除了 auto ,其他都与 align-items 属性完全一致。 // 该属性可能取 6 个值,除了 auto ,其他都与 align-items 属性完全一致。
// ------------------------------------------------------------ // ------------------------------------------------------------
\ No newline at end of file
@mixin fontFamily($font){ @mixin fontFamily($font) {
font-family: $font, "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", font-family: $font, "Helvetica Neue", Helvetica, "PingFang SC",
"Microsoft YaHei", "微软雅黑", Arial, sans-serif;; "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
} }
\ No newline at end of file
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
$active: #09f; $active: #09f;
$white: #fff; $white: #fff;
$black: #000; $black: #000;
$red: #FF2121; $red: #ff2121;
$redprice: #ff3131; $redprice: #ff3131;
$color_333: #333; $color_333: #333;
$color_555: #555; $color_555: #555;
...@@ -12,14 +12,13 @@ $color_666: #666; ...@@ -12,14 +12,13 @@ $color_666: #666;
$color_888: #888; $color_888: #888;
$color_999: #999; $color_999: #999;
$color_606: #606060; $color_606: #606060;
$color_bbb: #BBB; $color_bbb: #bbb;
$color_202426: #202426; $color_202426: #202426;
$color_4B4B4B: #4B4B4B; $color_4B4B4B: #4b4b4b;
$color_FE2F2F: #FE2F2F; $color_FE2F2F: #fe2f2f;
$color_FF4000: #FF4000; $color_FF4000: #ff4000;
$color_FF0000: #FF0000; $color_FF0000: #ff0000;
$color_525B65: #525B65; $color_525B65: #525b65;
/* /*
* @ 文字大小 * @ 文字大小
...@@ -35,48 +34,46 @@ $font_16: 16px; ...@@ -35,48 +34,46 @@ $font_16: 16px;
$font_14: 14px; $font_14: 14px;
$font_12: 12px; $font_12: 12px;
/* /*
* @ 背景颜色 * @ 背景颜色
*/ */
$bg_active: #09f; $bg_active: #09f;
$bg_0078FF: #0078FF; $bg_0078FF: #0078ff;
$bg_0080FF: #0080FF; $bg_0080FF: #0080ff;
$bg_fff: #fff; $bg_fff: #fff;
$bg_000: #000; $bg_000: #000;
$bg_f4f4f4: #f4f4f4; $bg_f4f4f4: #f4f4f4;
$bg_f5f5f5: #f5f5f5; $bg_f5f5f5: #f5f5f5;
$bg_f7f9fc: #f7f9fc; $bg_f7f9fc: #f7f9fc;
$bg_EBEFF5: #EBEFF5; $bg_EBEFF5: #ebeff5;
$bg_ccc: #ccc; $bg_ccc: #ccc;
$bg_333: #333; $bg_333: #333;
$bg_666: #666; $bg_666: #666;
$bg_999: #999; $bg_999: #999;
$bg_82BBFB: #82BBFB; $bg_82BBFB: #82bbfb;
$bg_EBC05A: #EBC05A; $bg_EBC05A: #ebc05a;
$bg_EBA216: #EBA216; $bg_EBA216: #eba216;
$bg_ff9898: #ff9898; $bg_ff9898: #ff9898;
$bg_ff3131: #ff3131; $bg_ff3131: #ff3131;
$bg_FFF8EB: #FFF8EB; $bg_FFF8EB: #fff8eb;
$bg_FFE400: #FFE400; $bg_FFE400: #ffe400;
$bg_FFA200: #FFA200; $bg_FFA200: #ffa200;
$bg_FE2F2F: #FE2F2F; $bg_FE2F2F: #fe2f2f;
$bg_FADD29: #FADD29; $bg_FADD29: #fadd29;
$bg_E7E7E7: #E7E7E7; $bg_E7E7E7: #e7e7e7;
$bg_18B4ED: #18B4ED; $bg_18B4ED: #18b4ed;
$bg_FF4000: #FF4000; $bg_FF4000: #ff4000;
$bg_FD7700: #FD7700; $bg_FD7700: #fd7700;
$bg_FCCD05: #FCCD05; $bg_FCCD05: #fccd05;
$bg_FF9500: #FF9500; $bg_FF9500: #ff9500;
$bg_077FD0: #077FD0; $bg_077FD0: #077fd0;
$bg_0198FE: #0198FE; $bg_0198FE: #0198fe;
$bg_F4AAA7: #F4AAA7; $bg_F4AAA7: #f4aaa7;
$bg_E02E24: #E02E24; $bg_E02E24: #e02e24;
$bg_007FD0: #007FD0; $bg_007FD0: #007fd0;
$bg_FF0000: #FF0000; $bg_FF0000: #ff0000;
$bg_FFF4CE: #FFF4CE; $bg_FFF4CE: #fff4ce;
$bg_FAFAFA: #FAFAFA; $bg_FAFAFA: #fafafa;
/* /*
* @ 分割线颜色 * @ 分割线颜色
...@@ -84,17 +81,15 @@ $bg_FAFAFA: #FAFAFA; ...@@ -84,17 +81,15 @@ $bg_FAFAFA: #FAFAFA;
$sp_ddd: #ddd; $sp_ddd: #ddd;
$sp_e7eaf1: #e7eaf1; $sp_e7eaf1: #e7eaf1;
/* /*
* @ 边框颜色 * @ 边框颜色
*/ */
$border_ddd: #ddd; $border_ddd: #ddd;
$border_e7eaf1: #E7EAF1; $border_e7eaf1: #e7eaf1;
$border_f31: #f31; $border_f31: #f31;
$border_ccc: #ccc; $border_ccc: #ccc;
/* /*
* @ 标签颜色 * @ 标签颜色
*/ */
$E0B97B: #E0B97B; $E0B97B: #e0b97b;
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
module.exports = function() { module.exports = function () {
return { return {
"@brand-primary": 'green', "@brand-primary": "green",
"@color-text-base": '#333' "@color-text-base": "#333",
} }
} }
\ No newline at end of file
import React, { Component } from 'react'; import React, { Component } from "react"
import CallApp from 'callapp-lib' import CallApp from "callapp-lib"
const options = { const options = {
scheme: { scheme: {
protocol: 'julyedu', protocol: "julyedu",
host: '', host: "",
port: '' port: "",
}, },
intent: { intent: {
package: "com.julyapp.julyonline", package: "com.julyapp.julyonline",
scheme: "julyedu", scheme: "julyedu",
action: 'julyapp.julyedu', action: "julyapp.julyedu",
category: 'category_julyedu' category: "category_julyedu",
}, },
universal: { universal: {
host: "api.julyedu.com/action", host: "api.julyedu.com/action",
pathKey: 'page' pathKey: "page",
}, },
appstore: "https://itunes.apple.com/cn/app/id1102275343?mt=8", appstore: "https://itunes.apple.com/cn/app/id1102275343?mt=8",
yingyongbao: "http://android.myapp.com/myapp/detail.htm?apkName=com.julyapp.julyonline", yingyongbao:
fallback: "http://android.myapp.com/myapp/detail.htm?apkName=com.julyapp.julyonline" "http://android.myapp.com/myapp/detail.htm?apkName=com.julyapp.julyonline",
fallback:
"http://android.myapp.com/myapp/detail.htm?apkName=com.julyapp.julyonline",
} }
class OpenApp extends Component { class OpenApp extends Component {
callApp = new CallApp(options)
callApp = new CallApp(options) // callApp = null
// callApp = null
static defaultProps = {
static defaultProps = { text: "在APP打开",
text: '在APP打开' }
}
handleClick = () => {
handleClick = () => { this.callApp.open({
this.callApp.open({ path: "",
path: '', param: {},
param: { callback: () => {
window.location.href =
}, "http://android.myapp.com/myapp/detail.htm?apkName=com.julyapp.julyonline"
callback: () => { },
window.location.href = "http://android.myapp.com/myapp/detail.htm?apkName=com.julyapp.julyonline"; })
} }
});
} render() {
return (
render() { <div className={this.props.className} onClick={this.handleClick}>
return ( {this.props.text}
<div className={this.props.className} onClick={this.handleClick}>{this.props.text}</div> </div>
); )
} }
} }
export default OpenApp; export default OpenApp
import React, { Component } from 'react'; import React, { Component } from "react"
import './index.scss' import "./index.scss"
import { initCaptchaNC } from "src/utils" import { initCaptchaNC } from "src/utils"
const appkey = 'FFFF0N000000000090FC' const appkey = "FFFF0N000000000090FC"
const scene = 'nc_login_h5' const scene = "nc_login_h5"
class CaptchaAli extends Component { class CaptchaAli extends Component {
nc = null nc = null
el = null el = null
state = { state = {
isLoaded: false isLoaded: false,
} }
componentDidMount() { componentDidMount() {
const _this = this const _this = this
this.el && initCaptchaNC(() => { this.el &&
const nc_token = [appkey, (new Date()).getTime(), Math.random()].join(':'); initCaptchaNC(() => {
this.nc = NoCaptcha.init({ const nc_token = [appkey, new Date().getTime(), Math.random()].join(":")
renderTo: '#nc', this.nc = NoCaptcha.init({
appkey, renderTo: "#nc",
scene, appkey,
token: nc_token, scene,
elementID: ['tel'], token: nc_token,
bannerHidden: false, elementID: ["tel"],
callback(data) { bannerHidden: false,
_this.props.onVerify({ callback(data) {
app_key: appkey, _this.props.onVerify({
scene, app_key: appkey,
token: nc_token, scene,
session_id: data.csessionid, token: nc_token,
sig: data.sig session_id: data.csessionid,
}) sig: data.sig,
}, })
error(s) { },
console.log(s) error(s) {
} console.log(s)
},
})
NoCaptcha.setEnabled(true)
this.nc.reset()
this.props.getInstance(this.nc)
}) })
NoCaptcha.setEnabled(true);
this.nc.reset()
this.props.getInstance(this.nc)
})
} }
render() { render() {
const {mb = 30} = this.props const { mb = 30 } = this.props
return ( return (
<div id={'captcha'} style={{marginBottom: `${mb}px`}}> <div id={"captcha"} style={{ marginBottom: `${mb}px` }}>
<div id="nc" ref={el => this.el = el}></div> <div id="nc" ref={(el) => (this.el = el)}></div>
</div> </div>
); )
} }
} }
export default CaptchaAli; export default CaptchaAli
\ No newline at end of file
...@@ -23,4 +23,4 @@ ...@@ -23,4 +23,4 @@
} }
} }
} }
} }
\ No newline at end of file
import React, { Component } from 'react'; import React, { Component } from "react"
import { initCaptcha } from 'src/utils'; import { initCaptcha } from "src/utils"
import { BarLoader } from 'react-spinners'; import { BarLoader } from "react-spinners"
import './index.scss'; import "./index.scss"
const CAPTCHAID = '6b0f5f6c8f334f3693ee754ba5692e36'
const CAPTCHAID = "6b0f5f6c8f334f3693ee754ba5692e36"
class Captcha extends Component { class Captcha extends Component {
state = {
isReady: false,
}
state = { componentDidMount() {
isReady: false const { getInstance, handleError, onVerify } = this.props
} const _this = this
const el = document.getElementById("captcha")
componentDidMount() { el &&
const {getInstance, handleError, onVerify} = this.props; initCaptcha(function () {
const _this = this; initNECaptcha(
const el = document.getElementById('captcha'); {
el && initCaptcha(function () { element: el,
initNECaptcha({ captchaId: CAPTCHAID,
element: el, mode: "float",
captchaId: CAPTCHAID, width: "auto",
mode: 'float', onReady: function (instance) {
width: 'auto', // 验证码一切准备就绪,此时可正常使用验证码的相关功能
onReady: function (instance) { _this.setState({
// 验证码一切准备就绪,此时可正常使用验证码的相关功能 isReady: true,
_this.setState({ })
isReady: true },
}); onVerify: function (err, data) {
}, onVerify(err, data)
onVerify: function (err, data) { },
onVerify(err,data) },
} (instance) => {
}, getInstance && getInstance(instance)
instance => { },
getInstance && getInstance(instance) (err) => {
}, handleError && handleError(err)
err => { }
handleError && handleError(err) )
} })
) }
})
}
render() { render() {
return ( return (
<div <div
className="captcha-container" className="captcha-container"
style={{ style={{
'marginBottom': this.props.mrBtm marginBottom: this.props.mrBtm,
}} }}
> >
{ {!this.state.isReady && (
!this.state.isReady && <div className="captcha-animation">
<div className="captcha-animation"> <BarLoader />
<BarLoader /> </div>
</div> )}
} <div
<div id={"captcha"}
id={'captcha'} style={{
style={{ marginBottom: this.props.mrBtm,
'marginBottom': this.props.mrBtm }}
}} />
/> </div>
</div> )
); }
}
} }
export default Captcha; export default Captcha
\ No newline at end of file
...@@ -13,4 +13,4 @@ ...@@ -13,4 +13,4 @@
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
\ No newline at end of file
import React from 'react' import React from "react"
import './index.scss' import "./index.scss"
const Tag = (props) => { const Tag = (props) => {
return ( return <span className={`state ${props.className}`}>{props.children}</span>
<span className={`state ${props.className}`}>
{props.children}
</span>
)
} }
export default Tag export default Tag
\ No newline at end of file
.state { .state {
box-sizing: border-box; box-sizing: border-box;
border-radius: 0 10px 10px 0; border-radius: 0 10px 10px 0;
padding: 1px 5px; padding: 1px 5px;
} }
\ No newline at end of file
@import "src/assets/css/variable"; @import "src/assets/css/variable";
.clearable-input-wrapper { .clearable-input-wrapper {
position: relative; position: relative;
input { input {
width: 300px; width: 300px;
height: 46px; height: 46px;
padding: 15px 0 15px 34px; padding: 15px 0 15px 34px;
border: 1px solid $border_ccc; border: 1px solid $border_ccc;
border-radius: 3px; border-radius: 3px;
-webkit-appearance: none; -webkit-appearance: none;
font-size: 15px; font-size: 15px;
&::-webkit-input-placeholder { &::-webkit-input-placeholder {
color: $color_999; color: $color_999;
font-size: $font_16; font-size: $font_16;
}
} }
}
.iconfont { .iconfont {
position: absolute; position: absolute;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
font-size: 21px; font-size: 21px;
color: $color_bbb; color: $color_bbb;
} }
.clear { .clear {
right: 13px; right: 13px;
} }
} }
\ No newline at end of file
import React, { Component } from 'react'; import React, { Component } from "react"
import './clearable-input.scss' import "./clearable-input.scss"
import classnames from 'classnames' import classnames from "classnames"
class ClearableInput extends Component { class ClearableInput extends Component {
render() {
render() { let {
let { value,
value, name,
name, wrapperClass,
wrapperClass, inputClass,
inputClass, type = "text",
type = 'text', icon,
icon, setFieldValue,
setFieldValue, ...rest
...rest } = this.props
} = this.props let clearIconStyle = {
let clearIconStyle = { display: value && value.length ? "block" : "none",
display: value && value.length ? 'block' : 'none'
}
return (
<div className={classnames('clearable-input-wrapper', wrapperClass)}>
<input
type={type}
value={value}
className={inputClass}
{...rest}
name={name}
/>
{icon}
<i
className={'iconfont icondanseshixintubiao-3 clear'}
onClick={() => {
setFieldValue(name,'')
}}
style={clearIconStyle}
/>
</div>
);
} }
return (
<div className={classnames("clearable-input-wrapper", wrapperClass)}>
<input
type={type}
value={value}
className={inputClass}
{...rest}
name={name}
/>
{icon}
<i
className={"iconfont icondanseshixintubiao-3 clear"}
onClick={() => {
setFieldValue(name, "")
}}
style={clearIconStyle}
/>
</div>
)
}
} }
export default ClearableInput; export default ClearableInput
\ No newline at end of file
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
border-radius: 4px; border-radius: 4px;
vertical-align: bottom; vertical-align: bottom;
} }
.course-status{ .course-status {
width: 100%; width: 100%;
height: 24px; height: 24px;
position: absolute; position: absolute;
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
line-height: 24px; line-height: 24px;
color: $white; color: $white;
font-size: 13px; font-size: 13px;
background-color: #E02E24; background-color: #e02e24;
opacity: .6; opacity: 0.6;
} }
.course-title { .course-title {
......
import React from 'react' import React from "react"
import './course.scss' import "./course.scss"
import {Link} from "react-router-dom" import { Link } from "react-router-dom"
const Course = (props) => { const Course = (props) => {
return ( return (
<li className={`course-item ${props.className}`}> <li className={`course-item ${props.className}`}>
{props.top} {props.top}
<a onClick={() => props.toDetail(props.id)}> <a onClick={() => props.toDetail(props.id)}>
{/* <Link to={`/detail?id=${props.id}`}> */} {/* <Link to={`/detail?id=${props.id}`}> */}
<img src={props.img} alt=""/> <img src={props.img} alt="" />
{props.status} {props.status}
<p className={`course-title ${props.className}`}>{props.title}</p> <p className={`course-title ${props.className}`}>{props.title}</p>
{/* </Link> */} {/* </Link> */}
</a> </a>
{props.bottom} {props.bottom}
</li> </li>
); )
}; }
export default Course;
export default Course
import React, {Component} from 'react' import React, { Component } from "react"
import './index.scss'; import "./index.scss"
import {withRouter} from 'react-router-dom' import { withRouter } from "react-router-dom"
import {browser,getParam} from "src/utils"; import { browser, getParam } from "src/utils"
class HeaderBar extends Component { class HeaderBar extends Component {
constructor(props) { constructor(props) {
super(props); super(props)
} }
goBack = () => { goBack = () => {
const {state, hash} = this.props.location const { state, hash } = this.props.location
if(hash.includes('goback')){ if (hash.includes("goback")) {
return window.history.go(-1) return window.history.go(-1)
}
if(browser.isWeixin && getParam('code') && getParam('state')){
window.history.go(-2)
}
if(state.records && state.records.length > 1){
window.history.go(-1);
}else if(state.from && state.from.pathname) {
location.replace(`${state.from.pathname}${state.from.search}`)
}else{
window.location.href = window.location.origin
}
} }
if (browser.isWeixin && getParam("code") && getParam("state")) {
toLink = () => { window.history.go(-2)
const { toHref } = this.props;
// console.log(toHref);
location.replace(toHref)
} }
if (state.records && state.records.length > 1) {
goShop = () => { window.history.go(-1)
this.props.history.push('/shopcart') } else if (state.from && state.from.pathname) {
location.replace(`${state.from.pathname}${state.from.search}`)
} else {
window.location.href = window.location.origin
} }
}
render() { toLink = () => {
const { toHref, home } = this.props; const { toHref } = this.props
return ( // console.log(toHref);
<div className="detail-header" style={{...this.props.style}}> location.replace(toHref)
{ }
!toHref && this.props.arrow &&
<i className='iconfont iconiconfront-68' onClick={this.props.goBack || this.goBack}></i> goShop = () => {
} this.props.history.push("/shopcart")
{ }
toHref && typeof toHref === 'function' &&
<i className='iconfont iconiconfront-68' onClick={toHref}></i> render() {
} const { toHref, home } = this.props
{ return (
toHref && typeof toHref === 'string' && <div className="detail-header" style={{ ...this.props.style }}>
<i className='iconfont iconiconfront-68' onClick={this.toLink}></i> {!toHref && this.props.arrow && (
} <i
<span className='herder'>{this.props.title}</span> className="iconfont iconiconfront-68"
{ onClick={this.props.goBack || this.goBack}
this.props.cart && ></i>
<i className='iconfont icongouwuche-xianxing' onClick={this.goShop}></i> )}
} {toHref && typeof toHref === "function" && (
{ <i className="iconfont iconiconfront-68" onClick={toHref}></i>
this.props.delete && )}
<i className='iconfont iconiconfront-56' onClick={this.props.toDelete}></i> {toHref && typeof toHref === "string" && (
} <i className="iconfont iconiconfront-68" onClick={this.toLink}></i>
{ )}
home && <span className="herder">{this.props.title}</span>
<i className="iconfont iconshouye-xianxing"></i> {this.props.cart && (
} <i
</div> className="iconfont icongouwuche-xianxing"
); onClick={this.goShop}
} ></i>
}; )}
{this.props.delete && (
<i
className="iconfont iconiconfront-56"
onClick={this.props.toDelete}
></i>
)}
{home && <i className="iconfont iconshouye-xianxing"></i>}
</div>
)
}
}
export default withRouter(HeaderBar); export default withRouter(HeaderBar)
...@@ -18,9 +18,8 @@ ...@@ -18,9 +18,8 @@
float: right; float: right;
} }
.herder { .herder {
font-size: 16px; font-size: 16px;
color: $color_202426; color: $color_202426;
} }
} }
\ No newline at end of file
import React, {Component} from 'react' import React, { Component } from "react"
import {SearchBar} from 'antd-mobile' import { SearchBar } from "antd-mobile"
import './index.scss' import "./index.scss"
class HeaderBar extends Component { class HeaderBar extends Component {
toSearch() { toSearch() {
window.location.href = '/search'; window.location.href = "/search"
} }
return() { return() {
window.location.href = '/'; window.location.href = "/"
} }
goShop = () => { goShop = () => {
const { isLogin = false } = this.props; const { isLogin = false } = this.props
let url = isLogin? '/shopcart' : '/passport'; let url = isLogin ? "/shopcart" : "/passport"
window.location.replace(url); window.location.replace(url)
} }
render() {
return (
<div className='preferential'>
<div className="search-nav">
{
!this.props.toHref &&
<i
className={'iconfont iconiconfront-68 return'}
onClick={this.return.bind(this)}
/>
}
{
this.props.toHref &&
<i
className={'iconfont iconiconfront-68 return'}
onClick={this.props.toHref}
/>
}
<SearchBar
placeholder="搜索课程"
cancelText={" "}
onFocus={this.toSearch.bind(this)}
showCancelButton={false}
/>
<i
className={'iconfont icongouwuche-xianxing shopping-cart'}
onClick={this.goShop}
/>
</div>
</div>
)
}
render() {
return (
<div className="preferential">
<div className="search-nav">
{!this.props.toHref && (
<i
className={"iconfont iconiconfront-68 return"}
onClick={this.return.bind(this)}
/>
)}
{this.props.toHref && (
<i
className={"iconfont iconiconfront-68 return"}
onClick={this.props.toHref}
/>
)}
<SearchBar
placeholder="搜索课程"
cancelText={" "}
onFocus={this.toSearch.bind(this)}
showCancelButton={false}
/>
<i
className={"iconfont icongouwuche-xianxing shopping-cart"}
onClick={this.goShop}
/>
</div>
</div>
)
}
} }
export default HeaderBar; export default HeaderBar
...@@ -12,7 +12,9 @@ ...@@ -12,7 +12,9 @@
background-color: $bg_f7f9fc; background-color: $bg_f7f9fc;
} }
.am-search-input, .am-search-synthetic-ph, .am-search-value { .am-search-input,
.am-search-synthetic-ph,
.am-search-value {
text-align: left; text-align: left;
padding-left: 15px; padding-left: 15px;
height: 26px; height: 26px;
...@@ -24,7 +26,8 @@ ...@@ -24,7 +26,8 @@
border-radius: 13px; border-radius: 13px;
} }
.shopping-cart, .return { .shopping-cart,
.return {
font-size: 20px; font-size: 20px;
} }
} }
\ No newline at end of file
import React, { Component } from 'react'; import React, { Component } from "react"
import ReactDOM from 'react-dom' import ReactDOM from "react-dom"
import { HashLoader } from "react-spinners"; import { HashLoader } from "react-spinners"
import './loading.scss' import "./loading.scss"
const container = document.body const container = document.body
class Loading extends Component { class Loading extends Component {
static defaultProps = {
text: "加载中",
fake: 0,
}
static defaultProps = { state = {
text: '加载中', isLoading: true,
fake: 0 }
}
state = { componentDidMount() {
isLoading: true if (!this.props.isLoading) {
this.setState({
isLoading: false,
})
} }
}
componentDidMount() { componentDidUpdate(prevProps) {
if(!this.props.isLoading){ let { isLoading, fake } = this.props
this.setState({ if (!isLoading) {
isLoading: false if (fake) {
}) setTimeout(() => {
this.setState({
isLoading,
})
}, fake)
} else {
if (prevProps.isLoading) {
this.setState(() => ({
isLoading: false,
}))
} }
}
} else {
if (prevProps.isLoading !== isLoading) {
this.setState(() => ({
isLoading: true,
}))
}
} }
}
render() {
const innerLoading = (
<div className="loading">
<div className="loading-wrapper">
<HashLoader
css={{
display: "block",
marginTop: "-100px",
}}
size={50}
color={"#09f"}
/>
<p>{this.props.text}</p>
</div>
</div>
)
componentDidUpdate(prevProps) { return this.state.isLoading
let {isLoading, fake} = this.props ? ReactDOM.createPortal(innerLoading, container)
if (!isLoading) { : this.props.children
if(fake){ }
setTimeout(() => {
this.setState({
isLoading
})
}, fake)
}else {
if(prevProps.isLoading) {
this.setState(()=>({
isLoading: false
}))
}
}
}else{
if(prevProps.isLoading !== isLoading) {
this.setState(()=>({
isLoading: true
}))
}
}
}
render() {
const innerLoading =
<div className="loading">
<div className="loading-wrapper">
<HashLoader
css={{
display: 'block',
marginTop: '-100px'
}}
size={50}
color={'#09f'}
/>
<p>{this.props.text}</p>
</div>
</div>
return (
this.state.isLoading ? ReactDOM.createPortal(innerLoading, container) : this.props.children
);
}
} }
export default Loading; export default Loading
\ No newline at end of file
@import "src/assets/css/variable"; @import "src/assets/css/variable";
.loading { .loading {
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
.loading-wrapper { .loading-wrapper {
display: flex; display: flex;
flex-flow: column; flex-flow: column;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
p { p {
font-size: 14px; font-size: 14px;
margin-top: 12px; margin-top: 12px;
color: $active; color: $active;
}
} }
} }
\ No newline at end of file }
import React, { Component } from 'react'; import React, { Component } from "react"
import ReactDOM from 'react-dom'; import ReactDOM from "react-dom"
import classnames from 'classnames'; import classnames from "classnames"
import './index.scss'; import "./index.scss"
const Root = document.querySelector('body'); const Root = document.querySelector("body")
const events = ['touchmove', 'mousewheel']; const events = ["touchmove", "mousewheel"]
class Mask extends Component { class Mask extends Component {
constructor(props) { constructor(props) {
super(props); super(props)
if(!this.el) { if (!this.el) {
this.el = document.createElement('div'); this.el = document.createElement("div")
} }
} }
componentDidMount() { componentDidMount() {
events.forEach(item => { events.forEach((item) => {
this.el.addEventListener(item, this.preventEvent, { this.el.addEventListener(item, this.preventEvent, {
passive: false passive: false,
}) })
}) })
Root.appendChild(this.el); Root.appendChild(this.el)
} }
componentWillUnmount() { componentWillUnmount() {
Root.removeChild(this.el); Root.removeChild(this.el)
} }
preventEvent = e => { preventEvent = (e) => {
e.preventDefault(); e.preventDefault()
} }
render() { render() {
const { visible, handleToHide, className } = this.props; const { visible, handleToHide, className } = this.props
if(visible) { if (visible) {
return ReactDOM.createPortal( return ReactDOM.createPortal(
( <div className="mask">
<div className="mask"> <div className={classnames("mask-content", className)}>
<div className={classnames("mask-content", className)}> {this.props.children}
{this.props.children} </div>
</div> <div className="mask-footer">
<div className="mask-footer"> <i className="mask-button__close" onClick={handleToHide}></i>
<i className="mask-button__close" onClick={handleToHide}></i>
</div>
</div> </div>
), </div>,
this.el this.el
); )
}else { } else {
return null; return null
} }
} }
} }
export default Mask; export default Mask
\ No newline at end of file
...@@ -8,8 +8,8 @@ ...@@ -8,8 +8,8 @@
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
color:#fff; color: #fff;
background-color: rgba(0, 0, 0, .5); background-color: rgba(0, 0, 0, 0.5);
z-index: 999; z-index: 999;
} }
...@@ -31,5 +31,5 @@ ...@@ -31,5 +31,5 @@
width: 33px; width: 33px;
height: 33px; height: 33px;
background-size: cover; background-size: cover;
background-image: url('https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/close-btn.png'); background-image: url("https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/close-btn.png");
} }
\ No newline at end of file
import React from 'react'; import React from "react"
import { NavLink, withRouter } from 'react-router-dom' import { NavLink, withRouter } from "react-router-dom"
import './index.scss' import "./index.scss"
const navLinkConfig = [ const navLinkConfig = [
{ {
to: '/', to: "/",
exact: true, exact: true,
icon: 'index-icon', icon: "index-icon",
activeIcon: 'index-icon-active', activeIcon: "index-icon-active",
text: '首页' text: "首页",
}, },
{ {
to: '/classify', to: "/classify",
exact: false, exact: false,
icon: 'classify-icon', icon: "classify-icon",
activeIcon: 'classify-icon-active', activeIcon: "classify-icon-active",
text: '分类' text: "分类",
}, },
{ {
to: '/study', to: "/study",
exact: false, exact: false,
icon: 'study-icon', icon: "study-icon",
activeIcon: 'study-icon-active', activeIcon: "study-icon-active",
text: '学习' text: "学习",
}, },
{ {
to: '/my', to: "/my",
exact: false, exact: false,
icon: 'my-icon', icon: "my-icon",
activeIcon: 'my-icon-active', activeIcon: "my-icon-active",
text: '我的' text: "我的",
} },
] ]
const NavBar = React.memo(({location}) => { const NavBar = React.memo(({ location }) => {
return ( return (
<div className="nav-bar"> <div className="nav-bar">
{ {navLinkConfig.map((item) => {
navLinkConfig.map(item => { let { icon, text, activeIcon, ...rest } = item
let {icon, text, activeIcon, ...rest} = item return (
return ( <NavLink
<NavLink activeClassName={"active"}
activeClassName={'active'} className={"nav-item"}
className={'nav-item'} key={icon}
key={icon} {...rest}
{...rest} >
> <i
<i className={`icon ${item.to.length > 1 ? location.pathname.startsWith(item.to) ? activeIcon : icon : location.pathname === item.to ? activeIcon : icon}`}/> className={`icon ${
<span>{text}</span> item.to.length > 1
</NavLink> ? location.pathname.startsWith(item.to)
) ? activeIcon
}) : icon
} : location.pathname === item.to
<div className={'click-able'}></div> ? activeIcon
</div> : icon
) }`}
/>
<span>{text}</span>
</NavLink>
)
})}
<div className={"click-able"}></div>
</div>
)
}) })
export default withRouter(NavBar) export default withRouter(NavBar)
\ No newline at end of file
...@@ -78,7 +78,3 @@ ...@@ -78,7 +78,3 @@
background-image: linear-gradient(0deg, #ddd 50%, transparent 50%); background-image: linear-gradient(0deg, #ddd 50%, transparent 50%);
} }
} }
import React from 'react'; import React from "react"
import './orderlist.scss'; import "./orderlist.scss"
import {Link} from "react-router-dom"; import { Link } from "react-router-dom"
/** /**
* @OrderList 组件内容 * @OrderList 组件内容
...@@ -13,30 +12,33 @@ import {Link} from "react-router-dom"; ...@@ -13,30 +12,33 @@ import {Link} from "react-router-dom";
* @constructor * @constructor
*/ */
const OrderItem = ({
const OrderItem = ({ info, tab, children, src,id, isaist, toDetail, ...restProps }) => { info,
return ( tab,
<div className='public-list-item'> children,
<div className="public-content"> src,
{tab} id,
<div className="public-cover" > isaist,
{/* <Link to={`/detail?id=${id}`}> */} toDetail,
<img src={src} alt="" onClick={() => toDetail(id)} /> ...restProps
{/* </Link> */} }) => {
{ return (
(isaist && <div className="public-list-item">
<span className='return_cash'></span> <div className="public-content">
) {tab}
} <div className="public-cover">
</div> {/* <Link to={`/detail?id=${id}`}> */}
{info} <img src={src} alt="" onClick={() => toDetail(id)} />
</div> {/* </Link> */}
{React.Children.map( {isaist && <span className="return_cash"></span>}
children, </div>
child => (child ? React.cloneElement(child, {}) : child) {info}
)} </div>
</div> {React.Children.map(children, (child) =>
) child ? React.cloneElement(child, {}) : child
)}
</div>
)
} }
export default OrderItem; export default OrderItem
\ No newline at end of file
...@@ -38,7 +38,6 @@ ...@@ -38,7 +38,6 @@
background: url("./image/return.icon.png") no-repeat; background: url("./image/return.icon.png") no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
} }
} }
.order-info { .order-info {
......
import React, { PureComponent } from 'react' import React, { PureComponent } from "react"
import './tag.scss' import "./tag.scss"
export default class Tag extends PureComponent { export default class Tag extends PureComponent {
render() { render() {
return ( return (
<span className={this.props.name} {...this.props}> <span className={this.props.name} {...this.props}>
{this.props.children} {this.props.children}
</span> </span>
) )
} }
} }
.tagLately, .tagHot { .tagLately,
display: block; .tagHot {
max-width: 100%; display: block;
overflow: hidden; max-width: 100%;
margin-right: 10px; overflow: hidden;
margin-bottom: 10px; margin-right: 10px;
font-size: 12px; margin-bottom: 10px;
padding: 4px 10px; font-size: 12px;
border-radius: 14px; padding: 4px 10px;
float: left; border-radius: 14px;
background-color: #F5F5F5; float: left;
line-height: 20px; background-color: #f5f5f5;
color: #666; line-height: 20px;
color: #666;
} }
.tagHot { .tagHot {
color: #333; color: #333;
} }
\ No newline at end of file
import React, {Component} from 'react' import React, { Component } from "react"
import './index.scss' import "./index.scss"
import {http} from 'src/utils' import { http } from "src/utils"
class UserGift extends Component { class UserGift extends Component {
state = { state = {
user_gift: '' user_gift: "",
} }
componentDidMount() { componentDidMount() {
http.get(`${API['base-api']}/web/home/popup`).then((res) => { http.get(`${API["base-api"]}/web/home/popup`).then((res) => {
const {errno, data} = res.data const { errno, data } = res.data
if (errno === 200) { if (errno === 200) {
this.setState({ this.setState({
user_gift: data.new_user_gift.prize_img user_gift: data.new_user_gift.prize_img,
})
}
}) })
} }
})
}
get_newerModal = () => { get_newerModal = () => {
this.props.get_newerModal() this.props.get_newerModal()
} }
close = () => { close = () => {
this.props.close() this.props.close()
} }
render() { render() {
const {user_gift} = this.state const { user_gift } = this.state
return ( return (
<div className={'user-gift-popup'}> <div className={"user-gift-popup"}>
<div className={'user-gift-bgimg'}> <div className={"user-gift-bgimg"}>
<img onClick={this.get_newerModal} src={user_gift} alt=""/> <img onClick={this.get_newerModal} src={user_gift} alt="" />
<img className={'close_gift_box'} <img
src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/tinypng-common/close_icon.png" className={"close_gift_box"}
onClick={this.close} alt=""/> src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/tinypng-common/close_icon.png"
</div> onClick={this.close}
</div> alt=""
) />
} </div>
</div>
)
}
} }
export default UserGift export default UserGift
\ No newline at end of file
.user-gift-popup { .user-gift-popup {
background: rgba(0, 0, 0, .3); background: rgba(0, 0, 0, 0.3);
position: fixed; position: fixed;
left: 0; left: 0;
top: 0; top: 0;
...@@ -21,4 +21,4 @@ ...@@ -21,4 +21,4 @@
bottom: -45px; bottom: -45px;
} }
} }
} }
\ No newline at end of file
import React from 'react'; import React from "react"
import './index.scss' import "./index.scss"
const VList = (props) => { const VList = (props) => {
return ( return (
<li <li
className='v-list-item' className="v-list-item"
onClick={() => { onClick={() => {
typeof props.toDetail === 'function' && props.toDetail(props.id) typeof props.toDetail === "function" && props.toDetail(props.id)
}} }}
> >
<div className="content"> <div className="content">
<div className="cover"> <div className="cover">
{props.status} {props.status}
{props.courseExpire} {props.courseExpire}
{props.toDetail {props.toDetail ? (
? (<img src={props.img} alt=""/>) <img src={props.img} alt="" />
: (<img src={props.img} alt=""/>) ) : (
} <img src={props.img} alt="" />
</div> )}
{props.info} </div>
</div> {props.info}
{props.tab} </div>
</li> {props.tab}
); </li>
}; )
}
export default VList; export default VList
\ No newline at end of file
@import "src/assets/css/variable"; @import "src/assets/css/variable";
.v-list-item { .v-list-item {
height: 138px; height: 138px;
padding: 15px 15px 0; padding: 15px 15px 0;
.content { .content {
display: flex; display: flex;
height: 100%; height: 100%;
padding-bottom: 15px; padding-bottom: 15px;
border-bottom: 1px solid $sp_e7eaf1; border-bottom: 1px solid $sp_e7eaf1;
.cover {
flex: 0 0 auto;
margin-right: 16px;
position: relative;
width: 150px;
img {
width: 150px;
height: 108px;
border-radius: 4px;
}
}
.course-status {
width: 100%;
height: 24px;
position: absolute;
bottom: -1px;
border-radius: 0 0 3px 3px;
text-align: center;
line-height: 24px;
color: $white;
font-size: 13px;
}
.cover {
flex: 0 0 auto;
margin-right: 16px;
position: relative;
width: 150px;
img {
width: 150px;
height: 108px;
border-radius: 4px;
}
}
.course-status {
width: 100%;
height: 24px;
position: absolute;
bottom: -1px;
border-radius: 0 0 3px 3px;
text-align: center;
line-height: 24px;
color: $white;
font-size: 13px;
} }
}
} }
import React, {Component} from 'react' import React, { Component } from "react"
import './index.scss' import "./index.scss"
class WxLogin extends Component { class WxLogin extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
} }
// 提示微信登录还是账号登录,微信授权登录不需要绑定手机号 // 提示微信登录还是账号登录,微信授权登录不需要绑定手机号
wxLogin = () => { wxLogin = () => {
let url = window.location.href let url = window.location.href
if (url.includes('code=') && url.includes('state=STATE')) { if (url.includes("code=") && url.includes("state=STATE")) {
let index = url.lastIndexOf('code=') let index = url.lastIndexOf("code=")
url = url.substr(0, index - 1) url = url.substr(0, index - 1)
}
window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx23dac6775ac82877&redirect_uri=" + encodeURIComponent(url + "&aa=bb").toLowerCase() + "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"
}
zhLogin = () => {
this.props.history.push('/passport')
} }
window.location.href =
"https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx23dac6775ac82877&redirect_uri=" +
encodeURIComponent(url + "&aa=bb").toLowerCase() +
"&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"
}
zhLogin = () => {
this.props.history.push("/passport")
}
render() { render() {
return ( return (
<div className="change-login-type"> <div className="change-login-type">
<div className="login-type-content"> <div className="login-type-content">
<div className="wx-login" onClick={this.wxLogin}> <div className="wx-login" onClick={this.wxLogin}>
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/mlCourse/m/wx-icon.png" alt=""/> <img
<span>微信登录</span> src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/mlCourse/m/wx-icon.png"
</div> alt=""
<div className="zh-login" onClick={this.zhLogin}> />
<img src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/mlCourse/m/zh-icon.png" alt=""/> <span>微信登录</span>
<span>账号登录</span> </div>
</div> <div className="zh-login" onClick={this.zhLogin}>
</div> <img
</div> src="https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/mlCourse/m/zh-icon.png"
) alt=""
} />
<span>账号登录</span>
</div>
</div>
</div>
)
}
} }
export default WxLogin export default WxLogin
.change-login-type { .change-login-type {
background: rgba(0, 0, 0, .5); background: rgba(0, 0, 0, 0.5);
position: fixed; position: fixed;
left: 0; left: 0;
top: 0; top: 0;
...@@ -22,7 +22,8 @@ ...@@ -22,7 +22,8 @@
justify-content: space-between; justify-content: space-between;
} }
.wx-login, .zh-login { .wx-login,
.zh-login {
text-align: center; text-align: center;
img { img {
width: 34px; width: 34px;
...@@ -31,9 +32,9 @@ ...@@ -31,9 +32,9 @@
span { span {
display: block; display: block;
color: #525C65; color: #525c65;
font-size: 14px; font-size: 14px;
margin-top: 3px; margin-top: 3px;
} }
} }
} }
\ No newline at end of file
import React from 'react' import React from "react"
import Address from "./index"; import Address from "./index"
import {text, boolean, withKnobs} from '@storybook/addon-knobs' import { text, boolean, withKnobs } from "@storybook/addon-knobs"
import {action} from '@storybook/addon-actions' import { action } from "@storybook/addon-actions"
export default { export default {
title: 'address', title: "address",
component: Address, component: Address,
decorators: [withKnobs] decorators: [withKnobs],
} }
let visible = true, let visible = true,
title = '收货地址', title = "收货地址",
subtitle = '获奖用户(以最终榜单为准)请及时填写收货信息', subtitle = "获奖用户(以最终榜单为准)请及时填写收货信息",
address = '金域国际中心', address = "金域国际中心",
phone = '1331234123', phone = "1331234123",
name = '某某某' name = "某某某"
export const Default = () => { export const Default = () => {
visible = boolean('visible', visible) visible = boolean("visible", visible)
name = text('name', name) name = text("name", name)
phone = text('phone', phone) phone = text("phone", phone)
address = text('address', address) address = text("address", address)
title = text('title(optional)', title) title = text("title(optional)", title)
subtitle = text('subtitle(optional)', subtitle) subtitle = text("subtitle(optional)", subtitle)
return <Address visible={visible} return (
subtitle={subtitle} <Address
title={title} visible={visible}
name={name} subtitle={subtitle}
phone={phone} title={title}
address={address} name={name}
onClose={action('onClose')} phone={phone}
validate={() => ({name: '姓名'})} address={address}
onError={(errors) => { onClose={action("onClose")}
console.log(errors);}} validate={() => ({ name: "姓名" })}
onSubmit={(values, formikHelpers) => { onError={(errors) => {
console.log(formikHelpers); console.log(errors)
}} }}
onSubmit={(values, formikHelpers) => {
console.log(formikHelpers)
}}
/> />
} )
\ No newline at end of file }
...@@ -12,14 +12,14 @@ ...@@ -12,14 +12,14 @@
.title { .title {
margin-bottom: 8px; margin-bottom: 8px;
color: #525C65; color: #525c65;
font-size: 16px; font-size: 16px;
} }
.subtitle { .subtitle {
margin-bottom: 15px; margin-bottom: 15px;
font-size: 13px; font-size: 13px;
color: #ED6A1D; color: #ed6a1d;
text-align: left; text-align: left;
} }
...@@ -27,13 +27,13 @@ ...@@ -27,13 +27,13 @@
width: 250px; width: 250px;
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
border: 1px solid #DDD; border: 1px solid #ddd;
font-size: 13px; font-size: 13px;
color: #999; color: #999;
outline: 0; outline: 0;
-webkit-appearance: none; -webkit-appearance: none;
&:nth-child(n+2) { &:nth-child(n + 2) {
margin-top: 10px; margin-top: 10px;
} }
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
border: 0; border: 0;
} }
.close{ .close {
position: absolute; position: absolute;
left: 50%; left: 50%;
bottom: -56px; bottom: -56px;
...@@ -63,4 +63,4 @@ ...@@ -63,4 +63,4 @@
font-size: 26px; font-size: 26px;
color: #fff; color: #fff;
} }
} }
\ No newline at end of file
import React, {Component} from 'react' import React, { Component } from "react"
import './index.scss' import "./index.scss"
import MaskCover from "../cover"; import MaskCover from "../cover"
import {Formik, Form, Field, FormikHelpers, FormikErrors} from "formik"; import { Formik, Form, Field, FormikHelpers, FormikErrors } from "formik"
interface PersonalInfo { interface PersonalInfo {
name: string name: string
phone: string phone: string
address: string address: string
} }
interface Props extends PersonalInfo { interface Props extends PersonalInfo {
subtitle?: string subtitle?: string
title?: string title?: string
onClose: () => void onClose: () => void
visible: boolean visible: boolean
validate: () => void validate: () => void
onSubmit: (values: PersonalInfo, formikHelpers: FormikHelpers<PersonalInfo>) => void onSubmit: (
onError: (errors: FormikErrors<PersonalInfo>) => void values: PersonalInfo,
formikHelpers: FormikHelpers<PersonalInfo>
) => void
onError: (errors: FormikErrors<PersonalInfo>) => void
} }
const Address: React.FC<Props> = ({name, phone, address, title, subtitle, visible, onClose, validate, onSubmit, onError}) => { const Address: React.FC<Props> = ({
return ( name,
visible ? phone,
<MaskCover> address,
<div className="common-address-container"> title,
<div className="title">{title}</div> subtitle,
<div className="subtitle">{subtitle}</div> visible,
<Formik initialValues={{name, phone, address}} onClose,
enableReinitialize={true} validate,
onSubmit={onSubmit} onSubmit,
validate={validate} onError,
> }) => {
{ return visible ? (
(props) => { <MaskCover>
if (props.errors) { <div className="common-address-container">
onError(props.errors) <div className="title">{title}</div>
} <div className="subtitle">{subtitle}</div>
return <Form className={'form'}> <Formik
<Field placeholder={'姓名'} name='name'></Field> initialValues={{ name, phone, address }}
<Field placeholder={'手机号'} name='phone'></Field> enableReinitialize={true}
<Field placeholder={'地址'} name='address'></Field> onSubmit={onSubmit}
<button type={'submit'} className={'submit'}>提交</button> validate={validate}
</Form> >
} {(props) => {
} if (props.errors) {
</Formik> onError(props.errors)
<i className={'iconfont iconiconfront-2 close'} onClick={onClose}></i> }
</div> return (
</MaskCover> <Form className={"form"}>
: null <Field placeholder={"姓名"} name="name"></Field>
) <Field placeholder={"手机号"} name="phone"></Field>
<Field placeholder={"地址"} name="address"></Field>
<button type={"submit"} className={"submit"}>
提交
</button>
</Form>
)
}}
</Formik>
<i className={"iconfont iconiconfront-2 close"} onClick={onClose}></i>
</div>
</MaskCover>
) : null
} }
Address.defaultProps = { Address.defaultProps = {
title: '收货信息', title: "收货信息",
subtitle: '获奖用户(以最终榜单为准)请及时填写收货信息' subtitle: "获奖用户(以最终榜单为准)请及时填写收货信息",
} }
export default Address export default Address
import React, { Component } from 'react'; import React, { Component } from "react"
import { http } from 'src/utils'; import { http } from "src/utils"
import { Formik, Form, Field } from 'formik'; import { Formik, Form, Field } from "formik"
import { Toast } from "antd-mobile"; import { Toast } from "antd-mobile"
import './index.scss'; import "./index.scss"
class AddressPopup extends Component { class AddressPopup extends Component {
constructor(props) { constructor(props) {
...@@ -10,22 +10,22 @@ class AddressPopup extends Component { ...@@ -10,22 +10,22 @@ class AddressPopup extends Component {
this.state = { this.state = {
isLoading: false, isLoading: false,
addressInfo: { addressInfo: {
name: '', name: "",
phone: '', phone: "",
address: '', address: "",
}, },
} }
} }
componentDidMount() { componentDidMount() {
this.fetchUserAddress(); this.fetchUserAddress()
} }
// 获取收货信息 // 获取收货信息
fetchUserAddress = () => { fetchUserAddress = () => {
const {addressInfo} = this.state; const { addressInfo } = this.state
http.get(`${API.home}/sys/user_address_info`).then(res => { http.get(`${API.home}/sys/user_address_info`).then((res) => {
const {code, data, msg} = res.data; const { code, data, msg } = res.data
if (code === 200) { if (code === 200) {
this.setState({ this.setState({
addressInfo: Object.assign({}, addressInfo, { addressInfo: Object.assign({}, addressInfo, {
...@@ -34,139 +34,137 @@ class AddressPopup extends Component { ...@@ -34,139 +34,137 @@ class AddressPopup extends Component {
address: data.address, address: data.address,
}), }),
isLoading: true, isLoading: true,
}); })
} }
}); })
} }
handleToSubmit = (params = {}) => { handleToSubmit = (params = {}) => {
const {successBindAddress} = this.props; const { successBindAddress } = this.props
http.post(`${API.home}/sys/update_address`, { http
act_type: 'treasure', .post(`${API.home}/sys/update_address`, {
...params, act_type: "treasure",
}).then(res => { ...params,
const {code, msg} = res.data; })
if (code === 200) { .then((res) => {
successBindAddress(); const { code, msg } = res.data
} else { if (code === 200) {
Toast.info(msg, 2, null, false); successBindAddress()
} } else {
}); Toast.info(msg, 2, null, false)
}
})
} }
render() { render() {
const {isLoading, addressInfo} = this.state; const { isLoading, addressInfo } = this.state
const {tip, prize, skip = 'default'} = this.props; const { tip, prize, skip = "default" } = this.props
return ( return (
<> <>
{ {isLoading && (
isLoading &&
<Formik <Formik
initialValues={{ initialValues={{
...addressInfo, ...addressInfo,
}} }}
validate={({name, phone, address}) => { validate={({ name, phone, address }) => {
const errors = {}; const errors = {}
if (!name) { if (!name) {
errors.name = '请输入收件人'; errors.name = "请输入收件人"
} }
if (!/^1[3-9]\d{9}$/.test(phone)) { if (!/^1[3-9]\d{9}$/.test(phone)) {
errors.phone = '请填写正确格式的手机号'; errors.phone = "请填写正确格式的手机号"
} }
if (!address) { if (!address) {
errors.address = '请输入收货地址'; errors.address = "请输入收货地址"
} }
return errors; return errors
}} }}
validateOnBlur={false} validateOnBlur={false}
validateOnChange={false} validateOnChange={false}
onSubmit={(values) => { onSubmit={(values) => {
this.handleToSubmit(values); this.handleToSubmit(values)
}} }}
> >
{ {({ errors }) => (
({errors}) => ( <Form className="address-form" data-skip={skip}>
<Form className="address-form" data-skip={skip}> <h2 className="address-form__title">收货信息</h2>
<h2 className="address-form__title">收货信息</h2> {prize ? (
{ <p className="address__prize">
prize ? ( 您抽中了
<p className='address__prize'> <span style={{ color: "#FF4000" }}>{prize}</span>
您抽中了 </p>
<span style={{'color': '#FF4000'}}>{prize}</span> ) : null}
</p> {tip ? (
) : (null) <div className="address-form__subtitle">{tip}</div>
} ) : (
{ <p className="address-form__desc">
tip ? (<div className="address-form__subtitle">{tip}</div>) : ( 请及时填写收货信息,获得实物奖品后将第一时间为您邮寄
<p className="address-form__desc">请及时填写收货信息,获得实物奖品后将第一时间为您邮寄</p>) </p>
} )}
<Field <Field
name="name" name="name"
render={({field}) => ( render={({ field }) => (
<div className="address-form__item"> <div className="address-form__item">
<input <input
{...field} {...field}
className="address-form__ipt" className="address-form__ipt"
type="text" type="text"
placeholder="收件人" placeholder="收件人"
/> />
{ {errors.name && (
errors.name && <p className="address-form__tip">{errors.name}</p>
<p className="address-form__tip">{errors.name}</p> )}
} </div>
</div> )}
)} />
/> <Field
<Field name="phone"
name="phone" render={({ field }) => (
render={({field}) => ( <div className="address-form__item">
<div className="address-form__item"> <input
<input {...field}
{...field} className="address-form__ipt"
className="address-form__ipt" type="text"
type="text" placeholder="联系方式"
placeholder="联系方式" />
/> {errors.phone && (
{ <p className="address-form__tip">{errors.phone}</p>
errors.phone && )}
<p className="address-form__tip">{errors.phone}</p> </div>
} )}
</div> />
)} <Field
/> name="address"
<Field render={({ field }) => (
name="address" <div className="address-form__item">
render={({field}) => ( <input
<div className="address-form__item"> {...field}
<input className="address-form__ipt"
{...field} type="text"
className="address-form__ipt" placeholder="收货地址"
type="text" />
placeholder="收货地址" {errors.address && (
/> <p className="address-form__tip">{errors.address}</p>
{ )}
errors.address && </div>
<p className="address-form__tip">{errors.address}</p> )}
} />
</div> <button
)} className="address-form__submit"
/> data-status="do"
<button type="submit"
className="address-form__submit" >
data-status="do" 提交
type="submit" </button>
>提交 </Form>
</button> )}
</Form>
)
}
</Formik> </Formik>
} )}
</> </>
); )
} }
} }
export default AddressPopup; export default AddressPopup
\ No newline at end of file
// 地址弹窗 // 地址弹窗
[data-skip="default"] { [data-skip="default"] {
.address-form__item { .address-form__item {
width: 250px; width: 250px;
} }
...@@ -15,13 +14,12 @@ ...@@ -15,13 +14,12 @@
background-color: rgba(82, 92, 101, 0.3); background-color: rgba(82, 92, 101, 0.3);
&[data-status="do"] { &[data-status="do"] {
background-color: #0099FF; background-color: #0099ff;
} }
} }
} }
[data-skip="year"] { [data-skip="year"] {
.address-form__title { .address-form__title {
margin: 10px 0 0; margin: 10px 0 0;
font-size: 18px; font-size: 18px;
...@@ -38,7 +36,7 @@ ...@@ -38,7 +36,7 @@
width: 270px; width: 270px;
margin: 0 15px 10px; margin: 0 15px 10px;
} }
.address-form__ipt { .address-form__ipt {
border-radius: 3px; border-radius: 3px;
} }
...@@ -50,7 +48,7 @@ ...@@ -50,7 +48,7 @@
border: 1px solid #090909; border: 1px solid #090909;
border-radius: 5px; border-radius: 5px;
color: #090909; color: #090909;
background-color: #FFE319; background-color: #ffe319;
} }
} }
...@@ -93,4 +91,4 @@ ...@@ -93,4 +91,4 @@
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;
outline: none; outline: none;
} }
\ No newline at end of file
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
} }
.popup-form__button--num { .popup-form__button--num {
border: 1px solid #99D6FF; border: 1px solid #99d6ff;
border-right-style: none; border-right-style: none;
border-radius: 6px 0 0 6px; border-radius: 6px 0 0 6px;
} }
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
} }
.popup-form__ipt { .popup-form__ipt {
border: 1px solid #99D6FF; border: 1px solid #99d6ff;
border-radius: 6px; border-radius: 6px;
&[data-type="tel"] { &[data-type="tel"] {
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
} }
.popup-form__button--num { .popup-form__button--num {
border: 1px solid #CDCDCD; border: 1px solid #cdcdcd;
border-right-style: none; border-right-style: none;
border-radius: 3px 0 0 3px; border-radius: 3px 0 0 3px;
} }
...@@ -74,7 +74,7 @@ ...@@ -74,7 +74,7 @@
} }
.popup-form__ipt { .popup-form__ipt {
border: 1px solid #CDCDCD; border: 1px solid #cdcdcd;
border-radius: 3px; border-radius: 3px;
&[data-type="tel"] { &[data-type="tel"] {
...@@ -117,11 +117,11 @@ ...@@ -117,11 +117,11 @@
.button { .button {
width: 40px; width: 40px;
height: 40px; height: 40px;
.icon{ .icon {
left: 5px; left: 5px;
} }
} }
.bg-green{ .bg-green {
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
} }
...@@ -162,7 +162,7 @@ ...@@ -162,7 +162,7 @@
&::after { &::after {
display: block; display: block;
content: ''; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
...@@ -170,7 +170,7 @@ ...@@ -170,7 +170,7 @@
width: 1px; width: 1px;
height: 14px; height: 14px;
margin: auto 0; margin: auto 0;
background-color: #AAAAAA; background-color: #aaaaaa;
} }
.iconfont { .iconfont {
...@@ -196,11 +196,11 @@ ...@@ -196,11 +196,11 @@
.popup-form__tip { .popup-form__tip {
margin: 0; margin: 0;
font-size: 12px; font-size: 12px;
color: #FF3131; color: #ff3131;
.iconfont { .iconfont {
font-size: 15px; font-size: 15px;
color: #FF3131; color: #ff3131;
} }
} }
...@@ -208,7 +208,7 @@ ...@@ -208,7 +208,7 @@
width: 110px; width: 110px;
height: 36px; height: 36px;
padding: 0; padding: 0;
border: 1px solid #E5E5E5; border: 1px solid #e5e5e5;
box-sizing: border-box; box-sizing: border-box;
font-size: 13px; font-size: 13px;
color: #999; color: #999;
...@@ -216,9 +216,9 @@ ...@@ -216,9 +216,9 @@
line-height: 36px; line-height: 36px;
background-color: transparent; background-color: transparent;
&[data-status='do'] { &[data-status="do"] {
border-color: #0099FF; border-color: #0099ff;
color: #0099FF; color: #0099ff;
} }
} }
...@@ -227,17 +227,17 @@ ...@@ -227,17 +227,17 @@
padding: 0; padding: 0;
margin: 0 auto; margin: 0 auto;
border-style: none; border-style: none;
color: #2B2B2B; color: #2b2b2b;
background-color: #F9DB4A; background-color: #f9db4a;
cursor: pointer; cursor: pointer;
&[data-status="done"] { &[data-status="done"] {
color: #fff; color: #fff;
background-color: #ABABAB; background-color: #ababab;
} }
&[data-status='do'] { &[data-status="do"] {
color: #fff; color: #fff;
background-color: #0099FF; background-color: #0099ff;
} }
} }
\ No newline at end of file
.closable-popup-mask { .closable-popup-mask {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(0, 0, 0, 0.6); background: rgba(0, 0, 0, 0.6);
z-index: 999; z-index: 999;
.popup-container { .popup-container {
position: absolute; position: absolute;
top: 165px; top: 165px;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
width: 300px; width: 300px;
padding: 20px 10px; padding: 20px 10px;
border-radius: 10px; border-radius: 10px;
background: #fff; background: #fff;
.title { .title {
font-size: 16px; font-size: 16px;
color: #525C65; color: #525c65;
text-align: center; text-align: center;
} }
.close { .close {
position: absolute; position: absolute;
bottom: -74px; bottom: -74px;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
font-size: 36px; font-size: 36px;
color: #fff; color: #fff;
} }
.close-icon { .close-icon {
position: absolute; position: absolute;
bottom: -66px; bottom: -66px;
left: 50%; left: 50%;
width: 33px; width: 33px;
height: 33px; height: 33px;
transform: translateX(-50%); transform: translateX(-50%);
font-size: 36px; font-size: 36px;
color: #fff; color: #fff;
}
} }
}
} }
import React from 'react' import React from "react"
import ReactDOM from 'react-dom' import ReactDOM from "react-dom"
import './index.scss' import "./index.scss"
import classnames from 'classnames' import classnames from "classnames"
const re = /(https?|ftp):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/ const re = /(https?|ftp):\/\/[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/
interface Props { interface Props {
title: string title: string
content: React.ReactNode content: React.ReactNode
className?: string className?: string
closable?: boolean closable?: boolean
clickMaskClose?: boolean clickMaskClose?: boolean
close?: () => void | Promise<void> close?: () => void | Promise<void>
closeIcon?: string closeIcon?: string
remove?: () => void remove?: () => void
afterClose?: () => void afterClose?: () => void
} }
function ClosablePopup({ function ClosablePopup({
title, title,
content, content,
className, className,
closable = true, closable = true,
close = function () { close = function () {},
}, clickMaskClose = true,
clickMaskClose = true, closeIcon = "iconiconfront-2",
closeIcon = 'iconiconfront-2', afterClose = function () {},
afterClose = function () { remove = function () {},
},
remove = function () {
}
}: Props) { }: Props) {
function unmountComponent() {
function unmountComponent() { ReactDOM.unmountComponentAtNode(div)
ReactDOM.unmountComponentAtNode(div) if (div && div.parentNode) {
if (div && div.parentNode) { div.parentNode.removeChild(div)
div.parentNode.removeChild(div)
}
} }
}
function _close() { function _close() {
let _c = close() let _c = close()
if (_c && _c.then) { if (_c && _c.then) {
_c.then(() => { _c.then(() => {
unmountComponent() unmountComponent()
afterClose() afterClose()
}) })
} else { } else {
unmountComponent() unmountComponent()
afterClose() afterClose()
}
} }
}
function clickMask() { function clickMask() {
if (closable) { if (closable) {
return return
}
if (!clickMaskClose) {
return
}
_close()
} }
if (!clickMaskClose) {
return
}
_close()
}
const closablePopup = ( const closablePopup = (
<div className={'closable-popup-mask'} onClick={clickMask}> <div className={"closable-popup-mask"} onClick={clickMask}>
<div className={classnames(['popup-container', className])}> <div className={classnames(["popup-container", className])}>
<div className="title">{title}</div> <div className="title">{title}</div>
<div className="content"> <div className="content">{content}</div>
{content} {closable &&
</div> (re.test(closeIcon) ? (
{ <img
closable && src={closeIcon}
(re.test(closeIcon) alt=""
? <img src={closeIcon} alt="" className={'close-icon'} onClick={_close} /> className={"close-icon"}
: <i className={`close iconfont ${closeIcon}`} onClick={_close} />) onClick={_close}
} />
</div> ) : (
</div> <i className={`close iconfont ${closeIcon}`} onClick={_close} />
) ))}
const div = document.createElement('div') </div>
document.body.appendChild(div) </div>
)
const div = document.createElement("div")
document.body.appendChild(div)
ReactDOM.render(closablePopup, div) ReactDOM.render(closablePopup, div)
return { return {
close: _close, close: _close,
remove: unmountComponent remove: unmountComponent,
} }
} }
export default ClosablePopup export default ClosablePopup
\ No newline at end of file
import React, { Component } from 'react'; import React, { Component } from "react"
import { Toast } from 'antd-mobile'; import { Toast } from "antd-mobile"
import { http } from 'src/utils'; import { http } from "src/utils"
import './index.scss'; import "./index.scss"
class ConfirmPhone extends Component { class ConfirmPhone extends Component {
continueBindPhone = () => { continueBindPhone = () => {
const { data, successBindPhone } = this.props; const { data, successBindPhone } = this.props
http.post( http
`${API.home}/sys/v2/user/bindMobile`, .post(`${API.home}/sys/v2/user/bindMobile`, {
{
...data, ...data,
type: 1, // 1:绑定,2:修改绑定 type: 1, // 1:绑定,2:修改绑定
is_valid: 0, // is_valid 是否验证 1:验证(默认),0不验证 is_valid: 0, // is_valid 是否验证 1:验证(默认),0不验证
} })
).then(res => { .then((res) => {
const { code, msg } = res.data; const { code, msg } = res.data
if(code === 200 ) { if (code === 200) {
successBindPhone(); successBindPhone()
}else { } else {
Toast.info(msg, 2, null, false); Toast.info(msg, 2, null, false)
} }
}); })
} }
render() { render() {
const { const { bindInfo = {}, desc, skip = "year", handleToCancle } = this.props
bindInfo = { },
desc,
skip = 'year',
handleToCancle
} = this.props;
return ( return (
<div className="popup-bind" data-skip={skip}> <div className="popup-bind" data-skip={skip}>
<h2 className="popup-bind__title">绑定手机号</h2> <h2 className="popup-bind__title">绑定手机号</h2>
{ {desc ? (
desc <div className="popup-bind__desc">{desc}</div>
? <div className="popup-bind__desc">{desc}</div> ) : (
: <p className="popup-bind__desc">该手机号已绑定到以下账号,继续绑定将解除以下绑定状态</p> <p className="popup-bind__desc">
} 该手机号已绑定到以下账号,继续绑定将解除以下绑定状态
</p>
)}
<ul className="popup-bind__list"> <ul className="popup-bind__list">
{ {bindInfo["email"] && (
bindInfo['email'] &&
<li className="popup-bind__account"> <li className="popup-bind__account">
{/* 邮箱 */} {/* 邮箱 */}
<i className="popup-bind__icon" data-plat="mail"></i> <i className="popup-bind__icon" data-plat="mail"></i>
<p className="popup-bind__account--name">{bindInfo['email']}</p> <p className="popup-bind__account--name">{bindInfo["email"]}</p>
</li> </li>
} )}
{ {bindInfo["wechat_nickname"] && (
bindInfo['wechat_nickname'] &&
<li className="popup-bind__account"> <li className="popup-bind__account">
{/* wechat */} {/* wechat */}
<i className="popup-bind__icon" data-plat="wachat"></i> <i className="popup-bind__icon" data-plat="wachat"></i>
<p className="popup-bind__account--name">{bindInfo['wechat_nickname']}</p> <p className="popup-bind__account--name">
{bindInfo["wechat_nickname"]}
</p>
</li> </li>
} )}
{ {bindInfo["qq_nickname"] && (
bindInfo['qq_nickname'] &&
<li className="popup-bind__account"> <li className="popup-bind__account">
{/* qq */} {/* qq */}
<i className="popup-bind__icon" data-plat="qq"></i> <i className="popup-bind__icon" data-plat="qq"></i>
<p className="popup-bind__account--name">{bindInfo['qq_nickname']}</p> <p className="popup-bind__account--name">
{bindInfo["qq_nickname"]}
</p>
</li> </li>
} )}
{ {bindInfo["sina_nickname"] && (
bindInfo['sina_nickname'] &&
<li className="popup-bind__account"> <li className="popup-bind__account">
{/* 微博 */} {/* 微博 */}
<i className="popup-bind__icon" data-plat="sina"></i> <i className="popup-bind__icon" data-plat="sina"></i>
<p className="popup-bind__account--name">{bindInfo['sina_nickname']}</p> <p className="popup-bind__account--name">
{bindInfo["sina_nickname"]}
</p>
</li> </li>
} )}
</ul> </ul>
<div className="popup-bind__footer"> <div className="popup-bind__footer">
<button <button
className="popup-bind__button popup-bind__button--cancle" className="popup-bind__button popup-bind__button--cancle"
onClick={handleToCancle}>取消</button> onClick={handleToCancle}
>
取消
</button>
<button <button
className="popup-bind__button popup-bind__button--confirm" className="popup-bind__button popup-bind__button--confirm"
onClick={this.continueBindPhone}>继续绑定</button> onClick={this.continueBindPhone}
>
继续绑定
</button>
</div> </div>
</div> </div>
) )
} }
} }
export default ConfirmPhone; export default ConfirmPhone
\ No newline at end of file
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
.popup-bind__footer { .popup-bind__footer {
padding: 0 18px; padding: 0 18px;
} }
.popup-bind__button { .popup-bind__button {
width: 105px; width: 105px;
height: 30px; height: 30px;
...@@ -33,14 +33,14 @@ ...@@ -33,14 +33,14 @@
} }
.popup-bind__button--cancle { .popup-bind__button--cancle {
border: 1px solid #0099FF; border: 1px solid #0099ff;
color: #0099FF; color: #0099ff;
} }
.popup-bind__button--confirm { .popup-bind__button--confirm {
border-style: none; border-style: none;
color: #fff; color: #fff;
background-color: #0099FF; background-color: #0099ff;
} }
} }
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
.popup-bind__desc { .popup-bind__desc {
width: 269px; width: 269px;
font-size: 12px; font-size: 12px;
color: #FF2121; color: #ff2121;
line-height: 18px; line-height: 18px;
} }
...@@ -79,14 +79,14 @@ ...@@ -79,14 +79,14 @@
padding: 0 15px; padding: 0 15px;
margin-bottom: 14px; margin-bottom: 14px;
} }
.popup-bind__button { .popup-bind__button {
width: 130px; width: 130px;
height: 44px; height: 44px;
border-radius: 5px; border-radius: 5px;
font-size: 16px; font-size: 16px;
} }
.popup-bind__button--cancle { .popup-bind__button--cancle {
border: 1px solid #090909; border: 1px solid #090909;
color: #090909; color: #090909;
...@@ -95,11 +95,10 @@ ...@@ -95,11 +95,10 @@
.popup-bind__button--confirm { .popup-bind__button--confirm {
border: 1px solid #090909; border: 1px solid #090909;
color: #090909; color: #090909;
background-color: #FFE319; background-color: #ffe319;
} }
} }
.popup-bind { .popup-bind {
text-align: center; text-align: center;
} }
...@@ -119,8 +118,8 @@ ...@@ -119,8 +118,8 @@
margin: 0 52px; margin: 0 52px;
text-align: left; text-align: left;
&:nth-child(n+2) { &:nth-child(n + 2) {
border-top: 1px solid #E5E5E5; border-top: 1px solid #e5e5e5;
} }
} }
...@@ -135,19 +134,19 @@ ...@@ -135,19 +134,19 @@
background-position: center; background-position: center;
&[data-plat="mail"] { &[data-plat="mail"] {
background-image: url('https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/icon-mail.png'); background-image: url("https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/icon-mail.png");
} }
&[data-plat="sina"] { &[data-plat="sina"] {
background-image: url('https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/icon-sina.png'); background-image: url("https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/icon-sina.png");
} }
&[data-plat="qq"] { &[data-plat="qq"] {
background-image: url('https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/icon-qq.png'); background-image: url("https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/icon-qq.png");
} }
&[data-plat="wachat"] { &[data-plat="wachat"] {
background-image: url('https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/icon-wechat.png'); background-image: url("https://julyedu-cdn.oss-cn-beijing.aliyuncs.com/newyear20/H5/icon-wechat.png");
} }
} }
...@@ -165,4 +164,4 @@ ...@@ -165,4 +164,4 @@
.popup-bind__button--cancle { .popup-bind__button--cancle {
background-color: transparent; background-color: transparent;
} }
\ No newline at end of file
import React from 'react' import React from "react"
import './course-base.scss' import "./course-base.scss"
const Course = (props) => { const Course = (props) => {
return ( return (
<li className={`course-base-item ${props.className}`} onClick={props.handleClick.bind(this, props.id)}> <li
{props.top} className={`course-base-item ${props.className}`}
<img src={props.img} alt=""/> onClick={props.handleClick.bind(this, props.id)}
<p className="course-title">{props.title}</p> >
{props.bottom} {props.top}
</li> <img src={props.img} alt="" />
); <p className="course-title">{props.title}</p>
}; {props.bottom}
</li>
export default Course; )
}
export default Course
...@@ -51,4 +51,4 @@ ...@@ -51,4 +51,4 @@
width: 100%; width: 100%;
} }
} }
} }
\ No newline at end of file
import React, {ReactNode, ReactElement} from 'react'; import React, { ReactNode, ReactElement } from "react"
import {handleNavigation, Navigation} from "../index"; import { handleNavigation, Navigation } from "../index"
import {RequireAtLeastOne} from 'src/utils/types' import { RequireAtLeastOne } from "src/utils/types"
import {History} from "history"; import { History } from "history"
import './index.scss'
import "./index.scss"
export interface CoursePropsBasic { export interface CoursePropsBasic {
image: string image: string
title: string title: string
courseId: number courseId: number
status: ReactNode status: ReactNode
navigate?: (courseId: number) => void navigate?: (courseId: number) => void
history?: History history?: History
subtitle?: string subtitle?: string
tag?: ReactElement tag?: ReactElement
marketing?: string marketing?: string
} }
export type CourseProps = RequireAtLeastOne<CoursePropsBasic, 'history' | 'navigate'> export type CourseProps = RequireAtLeastOne<
CoursePropsBasic,
const CourseCardH: React.FC<CourseProps> = ({image, title, subtitle, status, navigate, courseId, tag, history, marketing}) => { "history" | "navigate"
return ( >
<div className={'course-card course-card-h'} onClick={(e) => {
if (navigate) { const CourseCardH: React.FC<CourseProps> = ({
handleNavigation({e, courseId, navigate}) image,
} else { title,
handleNavigation({e, courseId, history} as Navigation) subtitle,
} status,
}}> navigate,
<div className="show"> courseId,
<img src={image} alt={title}/> tag,
{marketing && <div className="marketing">{marketing}</div>} history,
{tag} marketing,
</div> }) => {
<div className="info"> return (
<div className="title">{title}</div> <div
{subtitle && <div className="subtitle">{subtitle}</div>} className={"course-card course-card-h"}
<div className="status"> onClick={(e) => {
{status} if (navigate) {
</div> handleNavigation({ e, courseId, navigate })
</div> } else {
</div> handleNavigation({ e, courseId, history } as Navigation)
); }
}}
>
<div className="show">
<img src={image} alt={title} />
{marketing && <div className="marketing">{marketing}</div>}
{tag}
</div>
<div className="info">
<div className="title">{title}</div>
{subtitle && <div className="subtitle">{subtitle}</div>}
<div className="status">{status}</div>
</div>
</div>
)
} }
CourseCardH.displayName = 'CourseCardH' CourseCardH.displayName = "CourseCardH"
export default CourseCardH; export default CourseCardH
\ No newline at end of file
...@@ -20,11 +20,11 @@ ...@@ -20,11 +20,11 @@
.title { .title {
margin-bottom: 30px; margin-bottom: 30px;
font-size: 15px; font-size: 15px;
color: #525C65; color: #525c65;
display: -webkit-box; display: -webkit-box;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
overflow: hidden; overflow: hidden;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
@include fontFamily('PingFangSC-Regular'); @include fontFamily("PingFangSC-Regular");
} }
} }
\ No newline at end of file
import React from 'react' import React from "react"
import {CourseProps} from '../course-card-h' import { CourseProps } from "../course-card-h"
import {handleNavigation, Navigation} from "../index"; import { handleNavigation, Navigation } from "../index"
import './index.scss' import "./index.scss"
const CourseCardV: React.FC<CourseProps> = ({title, image, courseId, status, tag, navigate, history, marketing}) => { const CourseCardV: React.FC<CourseProps> = ({
return <div className={'course-card-v'} onClick={(e) => { title,
navigate ? handleNavigation({e, courseId, navigate}) image,
: handleNavigation({e, courseId, history} as Navigation) courseId,
}}> status,
<div className="show"> tag,
<img src={image} alt={title}/> navigate,
{marketing && <div className="marketing">{marketing}</div>} history,
{tag} marketing,
</div> }) => {
<div className="title">{title}</div> return (
<div className="status">{status}</div> <div
className={"course-card-v"}
onClick={(e) => {
navigate
? handleNavigation({ e, courseId, navigate })
: handleNavigation({ e, courseId, history } as Navigation)
}}
>
<div className="show">
<img src={image} alt={title} />
{marketing && <div className="marketing">{marketing}</div>}
{tag}
</div>
<div className="title">{title}</div>
<div className="status">{status}</div>
</div> </div>
)
} }
export default CourseCardV export default CourseCardV
\ No newline at end of file
import React from "react"; import React from "react"
import {withKnobs, number, text} from "@storybook/addon-knobs"; import { withKnobs, number, text } from "@storybook/addon-knobs"
import {action} from "@storybook/addon-actions"; import { action } from "@storybook/addon-actions"
import CourseCardH from "./course-card-h"
import CourseCardH from "./course-card-h"; import CourseCardV from "./course-card-v"
import CourseCardV from "./course-card-v";
export const courseData = { export const courseData = {
courseId: 140, courseId: 140,
title: '三月面试求职班', title: "三月面试求职班",
subtitle: '搞定算法 直通BAT', subtitle: "搞定算法 直通BAT",
image: 'https://julyedu-img-public.oss-cn-beijing.aliyuncs.com/Public/Image/20a86c1353.jpg', image:
marketing: '拼团减100元' "https://julyedu-img-public.oss-cn-beijing.aliyuncs.com/Public/Image/20a86c1353.jpg",
marketing: "拼团减100元",
} }
export default { export default {
title: 'course-card', title: "course-card",
component: CourseCardH, component: CourseCardH,
decorators: [withKnobs, (story: () => React.ReactElement) => <div className={'shadow'}>{story()}</div>], decorators: [
excludeStories: /.*Data$/ withKnobs,
(story: () => React.ReactElement) => (
<div className={"shadow"}>{story()}</div>
),
],
excludeStories: /.*Data$/,
} }
let {title, courseId, image, subtitle, marketing} = courseData let { title, courseId, image, subtitle, marketing } = courseData
let navigate = action(`navigate to /detail?id=${courseId}`) let navigate = action(`navigate to /detail?id=${courseId}`)
const status = <button className={'purchase'}>立即购买</button> const status = <button className={"purchase"}>立即购买</button>
export const Default = () => { export const Default = () => {
title = text('title', title) title = text("title", title)
subtitle = text('subtitle', subtitle) subtitle = text("subtitle", subtitle)
image = text('image', image) image = text("image", image)
courseId = number('courseId', courseId) courseId = number("courseId", courseId)
marketing = text('marketing', marketing) marketing = text("marketing", marketing)
return <CourseCardH title={title} status={status} courseId={courseId} subtitle={subtitle} image={image} return (
navigate={navigate} marketing={marketing}></CourseCardH> <CourseCardH
title={title}
status={status}
courseId={courseId}
subtitle={subtitle}
image={image}
navigate={navigate}
marketing={marketing}
></CourseCardH>
)
} }
export const V = () => { export const V = () => {
title = text('title', title) title = text("title", title)
image = text('image', image) image = text("image", image)
courseId = number('courseId', courseId) courseId = number("courseId", courseId)
return <CourseCardV image={image} courseId={courseId} status={status} title={title} return (
navigate={navigate} marketing={marketing}></CourseCardV> <CourseCardV
} image={image}
\ No newline at end of file courseId={courseId}
status={status}
title={title}
navigate={navigate}
marketing={marketing}
></CourseCardV>
)
}
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
height: 24px; height: 24px;
color: #fff; color: #fff;
font-size: 13px; font-size: 13px;
background: rgba(224, 46, 36, .6); background: rgba(224, 46, 36, 0.6);
text-align: center; text-align: center;
line-height: 24px; line-height: 24px;
} }
\ No newline at end of file
import React from "react"; import React from "react"
import { History } from "history"; import { History } from "history"
import { RequireAtLeastOne } from 'src/utils/types' import { RequireAtLeastOne } from "src/utils/types"
import CourseCardV from "./course-card-v";
import CourseCardH from "./course-card-h";
import CourseCardV from "./course-card-v"
import CourseCardH from "./course-card-h"
interface BaseNavigation { interface BaseNavigation {
e: React.MouseEvent e: React.MouseEvent
courseId: number courseId: number
navigate?: (courseId: number) => void navigate?: (courseId: number) => void
history?: History history?: History
} }
export type Navigation = RequireAtLeastOne<BaseNavigation, 'history' | 'navigate'> export type Navigation = RequireAtLeastOne<
BaseNavigation,
"history" | "navigate"
>
export const handleNavigation: (navigationArgs: Navigation) => void = ({ e, courseId, navigate, history }) => { export const handleNavigation: (navigationArgs: Navigation) => void = ({
const _n = navigate || function (courseId: number) { e,
history!.push(`/detail?id=${courseId}`) courseId,
} navigate,
let nodeName = (e.target as HTMLElement).nodeName.toLowerCase() history,
if (nodeName === 'a' || nodeName === 'button') { }) => {
return const _n =
navigate ||
function (courseId: number) {
history!.push(`/detail?id=${courseId}`)
} }
_n(courseId) let nodeName = (e.target as HTMLElement).nodeName.toLowerCase()
if (nodeName === "a" || nodeName === "button") {
return
}
_n(courseId)
} }
export const H = CourseCardH export const H = CourseCardH
export const V = CourseCardV export const V = CourseCardV
export default CourseCardH export default CourseCardH
\ No newline at end of file
...@@ -4,6 +4,6 @@ ...@@ -4,6 +4,6 @@
right: 0; right: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
background: rgba(0, 0, 0, .6); background: rgba(0, 0, 0, 0.6);
z-index: 999; z-index: 999;
} }
\ No newline at end of file
import React from 'react'; import React from "react"
import './index.scss' import "./index.scss"
const MaskCover:React.FC = ({children}) => { const MaskCover: React.FC = ({ children }) => {
return ( return <div className={"mask-cover"}>{children}</div>
<div className={'mask-cover'}> }
{children}
</div>
);
};
export default MaskCover; export default MaskCover
\ No newline at end of file
import React from 'react' import React from "react"
import { Modal } from 'antd-mobile' import { Modal } from "antd-mobile"
import './index.scss' import "./index.scss"
export default function ({ export default function ({
amount, amount,
limit_amount, limit_amount,
onCancel = () => { onCancel = () => {},
}, onConfirm,
onConfirm }) {
}) { const content = (
const content = ( <>
<> <div className="end-expansion-alert-ques">
<div className="end-expansion-alert-ques"> {`你的${amount}元优惠券正在膨胀中,
{`你的${amount}元优惠券正在膨胀中,
确定要结束膨胀吗?`} 确定要结束膨胀吗?`}
</div> </div>
<div className="end-expansion-alert-hint"> <div className="end-expansion-alert-hint">
{`离${limit_amount}元只差一点点了!继续膨胀,优惠更多哦`} {`离${limit_amount}元只差一点点了!继续膨胀,优惠更多哦`}
</div> </div>
</> </>
) )
Modal.alert('温馨提示', content, [ Modal.alert("温馨提示", content, [
{text: '再考虑下', onPress: onCancel, style: {color: '#333'}}, { text: "再考虑下", onPress: onCancel, style: { color: "#333" } },
{text: '确定结束膨胀', onPress: onConfirm} { text: "确定结束膨胀", onPress: onConfirm },
]) ])
} }
.am-modal { .am-modal {
width: 300px; width: 300px;
&-title { &-title {
font-size: 15px; font-size: 15px;
} }
} }
.end-expansion-alert { .end-expansion-alert {
&-ques { &-ques {
width: 200px; width: 200px;
margin: 0 auto 20px; margin: 0 auto 20px;
text-align: center; text-align: center;
color: #666; color: #666;
font-size: 15px; font-size: 15px;
} }
&-hint { &-hint {
color: #999; color: #999;
font-size: 12px; font-size: 12px;
} }
} }
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment