Commit 1435a144 authored by liuzhaoh's avatar liuzhaoh

DCTOM项目创建

parents
Pipeline #1222 canceled with stages
# .env.development 开发环境配置文件
# 开发环境
VITE_ENV = development
# 开发环境下,后端接口的基础URL
# base api
VITE_APP_BASE_API5 = '/vispc'
VITE_APP_BASE_MONITOR = '/monitor'
# VITE_APP_BASE_API5_PRI = https://vis.bmetech.com
\ No newline at end of file
# .env.development 开发环境配置文件
# 开发环境
VITE_ENV = production
# 开发环境下,后端接口的基础URL
# base api
VITE_APP_BASE_API5 = '/vispc'
VITE_APP_BASE_MONITOR = '/monitor'
VITE_APP_BASE_API5_PRI = https://vis.bmetech.com
\ No newline at end of file
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
{
"recommendations": ["Vue.volar"]
}
# Vue 3 + Vite
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
This diff is collapsed.
{
"name": "dctomproject",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --mode development",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"axios": "^1.9.0",
"crypto-js": "^4.2.0",
"element-plus": "^2.9.10",
"js-cookie": "^3.0.5",
"path": "^0.12.7",
"pinia": "^3.0.2",
"qs": "^6.14.0",
"vue": "^3.5.13",
"vue-router": "^4.5.1"
},
"devDependencies": {
"@types/node": "^22.15.18",
"@vitejs/plugin-vue": "^5.2.3",
"sass": "^1.88.0",
"sass-loader": "^16.0.5",
"vite": "^6.3.5"
}
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
\ No newline at end of file
<template>
<router-view />
</template>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
\ No newline at end of file
<script setup>
import { ref } from 'vue'
defineProps({
msg: String,
})
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Learn more about IDE Support for Vue in the
<a
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
target="_blank"
>Vue Docs Scaling up Guide</a
>.
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { router } from './router'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
createApp(App).use(router).use(ElementPlus).mount('#app')
import axios from "axios";
import { ElMessageBox, ElMessage } from "element-plus";
import {router} from "@/router";
import { getToken, clearAllCookies, removeToken } from "@/utils/auth";
// vite环境变量
const ENV = import.meta.env
const requestObj = {};
const service = axios.create({
headers: {
"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest"
}
});
service.interceptors.request.use(
config => {
if (getToken("TOKEN")) config.headers["TOKEN"] = getToken("TOKEN");
let dataBranchFactoryId = getToken("dataBranchFactoryId");
if (
dataBranchFactoryId &&
dataBranchFactoryId != "undefined" &&
dataBranchFactoryId != ""
) {
let urlString = config.url;
let str_url = urlString.split("?")[0];
if (str_url == "/user/captcha" || str_url == "/user/login") {
config.url = ENV.VITE_APP_BASE_API5 + config.url;
} else {
config.url = ENV.VITE_APP_BASE_API5 + config.url;
}
} else {
config.url = ENV.VITE_APP_BASE_API5 + config.url;
}
requestObj.url = config.url;
requestObj.param = config.params;
return config;
},
error => {
// console.error(`${error.message}`)
// error.message = `连接错误`;
return Promise.reject(error);
}
);
// response interceptor
service.interceptors.response.use(
/**
* If you want to get http information such as headers or status
* Please return response => response
*/
/**
* Determine the request status by custom code
* Here is just an example
* You can also judge the status by HTTP Status Code
*/
response => {
const res = response.data;
if (res.code !== 1) {
if (res.code === 1001) {
removeToken("dataBranchFactoryId");
// router.push(`login`);
} else {
if (!res.success) {
if (res.code === 9000) {
if (res.msg.indexOf("新密码输入不符合规则") >= 0) {
let tmpMsg = res.msg;
tmpMsg += "<br/> 1、长度8~20";
tmpMsg += "<br/> 2、必须包含字母(大写或者小写)";
tmpMsg += "<br/> 3、必须包含数字";
tmpMsg +=
"<br/> 4、包含特殊字符 ~!@#$%^&*()[]{}<>?+ 不能包含空格";
tmpMsg += "<br/> 5、只能包含以上特殊字符以及大小字母和数字的密码";
ElMessage({
dangerouslyUseHTMLString: true, //重点代码
message: tmpMsg,
type: "error",
duration: 5 * 1000
});
} else {
const msg = res.message || res.msg || "Error";
// msg 是否包含英文字符
if (msg.match(/[a-zA-Z]/)) {
console.error('错误接口',response);
console.error('错误信息',msg);
} else {
ElMessage({
message: res.message || res.msg || "Error",
type: "error",
duration: 5 * 1000
});
}
}
}
else if (
res.code === 3015 ||
res.code === 3017 ||
res.code === 3014 ||
res.code === 3016 ||
res.code === 9002
) {
// 数据下沉的状态码不做报错提示
} else {
requestExceptCap(response.config, response.status);
const msg = res.message || res.msg || "Error";
// msg 是否包含英文字符
if (msg.match(/[a-zA-Z]/)) {
console.error('错误接口',response);
console.error('错误信息',msg);
} else {
ElMessage({
message: res.message || res.msg || "Error",
type: "error",
duration: 5 * 1000
});
}
}
}
}
return res;
} else {
return res;
}
},
error => {
if (error && error.response) {
requestExceptCap(error.response.config, error.response.code);
switch (error.response.code) {
case 400:
error.message = "请求错误(400)";
break;
case 401:
error.message = "未授权,请登录(401)";
break;
case 403:
error.message = "拒绝访问(403)";
break;
case 404:
error.message = `请求地址出错: ${error.response.config.url}`;
break;
case 405:
error.message = "请求方法未允许(405)";
break;
case 408:
error.message = "请求超时(408)";
break;
case 500:
error.message = "服务器内部错误(500)";
break;
case 501:
error.message = "服务未实现(501)";
break;
case 502:
error.message = "网络错误(502)";
break;
case 503:
error.message = "服务不可用(503)";
break;
case 504:
error.message = "网络超时(504)";
break;
case 505:
error.message = "HTTP版本不受支持(505)";
break;
default:
error.message = `连接错误: ${error.message}`;
// console.error(error.message);
// error.message = `连接错误`;
}
} else {
error.message = "连接到服务器失败,请联系管理员";
}
requestExceptCap(error.config);
// ElMessage({
// message: error.message || error.msg,
// type: 'error',
// duration: 5 * 1000
// })
return Promise.reject(error);
}
);
/**
* @Description: 接口报错异常捕获
* @param {Object} capData 报错接口参数
* @param {string} code 报错编码
* @return void
* @author liqiuyu
* @date 2024/3/11
*/
function requestExceptCap(capData = {}, code = "Network Error") {
const date = new Date();
const time = format(date);
const url = "";
const link = capData.url;
let tempData;
if (!link || link.indexOf("/data/collect") !== -1) {
return;
}
if (capData.method == "post") {
tempData = capData.data || requestObj.param;
} else {
tempData = capData.param || requestObj.param;
}
if (typeof tempData == "object") {
tempData = JSON.stringify(tempData);
}
const param = {
firstLevelCode: "notOpen",
secondLevelCode: "apiError",
additionalInfo: {
code,
source: "PC",
mac: "未配置mac信息",
customerId: getToken("customerId"),
time: time,
url: link,
param: tempData
}
};
service({
url: "/data/collect",
data: param,
method: "post",
headers: {
"Content-Type": "application/json"
}
})
.then(res => {})
.catch(error => {});
}
function format(dataString) {
//dataString是整数,否则要parseInt转换
var time = new Date(dataString);
var year = time.getFullYear();
var month = time.getMonth() + 1;
var day = time.getDate();
var hour = time.getHours();
var minute = time.getMinutes();
var second = time.getSeconds();
return (
year +
"-" +
(month < 10 ? "0" + month : month) +
"-" +
(day < 10 ? "0" + day : day) +
" " +
(hour < 10 ? "0" + hour : hour) +
":" +
(minute < 10 ? "0" + minute : minute) +
":" +
(second < 10 ? "0" + second : second)
);
}
export default service;
import request from "./index.js";
import qs from "qs";
export function getData(url, pri) {
return request({
url,
method: "get"
});
}
export function getVisData(url) {
return request({
url,
method: "get"
});
}
export function http(method, url, data) {
return request({
url: url,
method,
data
// headers: {
// 'Content-Type': 'application/json'
// }
});
}
export function getDataFun(url, params) {
return request({
url,
method: "get",
params
});
}
export function postDataJSON(url, data) {
/*url.indexOf('admin') > -1 ? url = process.env.VUE_APP_BASE_API4 + url
: url = process.env.VUE_APP_BASE_API1 + url*/
return request({
url: url,
method: "post",
data: data,
headers: {
"Content-Type": "application/json"
}
});
}
export function postData(url, data) {
console.log(data);
// url.indexOf('admin') > -1 ? url = process.env.VUE_APP_BASE_API4 + url
// : url = process.env.VUE_APP_BASE_API1 + url
return request({
url: url,
method: "POST",
params: data
});
}
export function postDataFun(url, data) {
console.log(data);
// url.indexOf('admin') > -1 ? url = process.env.VUE_APP_BASE_API4 + url
// : url = process.env.VUE_APP_BASE_API1 + url
return request({
url: url,
method: "POST",
params: data,
headers: {
"content-type": "application/x-www-form-urlencoded"
}
});
}
export function putData(url, data) {
return request({
url: url,
method: "PUT",
params: data
});
}
export function deleteData(url, data) {
return request({
url: url,
method: "delete",
params: data
});
}
export function postDataJson(url, data) {
return request({
url: url,
method: "post",
data,
headers: {
"Content-Type": "application/json"
}
});
}
export function uploadFiles(method, url, data) {
return request({
url: url,
method,
data,
headers: {
"Content-Type": "multipart/form-data"
}
});
}
export function getDataScreen(url, params) {
return request({
url,
method: "get",
params
});
}
export function postDataScreen(url, data) {
return request({
url: url,
method: "post",
data: qs.stringify(data)
});
}
// 百度坐标转高德(传入经度、纬度)
export function bd_decrypt(bd_lng, bd_lat) {
var X_PI = (Math.PI * 3000.0) / 180.0;
var x = bd_lng - 0.0065;
var y = bd_lat - 0.006;
var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * X_PI);
var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI);
var gg_lng = z * Math.cos(theta);
var gg_lat = z * Math.sin(theta);
return { lng: gg_lng, lat: gg_lat };
}
// 高德坐标转百度(传入经度、纬度)
export function bd_encrypt(gg_lng, gg_lat) {
var X_PI = (Math.PI * 3000.0) / 180.0;
var x = gg_lng;
var y = gg_lat;
var z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * X_PI);
var theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * X_PI);
var bd_lng = z * Math.cos(theta) + 0.0065;
var bd_lat = z * Math.sin(theta) + 0.006;
return {
bd_lat: bd_lat,
bd_lng: bd_lng
};
}
function transformlat(lng, lat) {
const PI = 3.1415926535897932384626;
let ret =
-100.0 +
2.0 * lng +
3.0 * lat +
0.2 * lat * lat +
0.1 * lng * lat +
0.2 * Math.sqrt(Math.abs(lng));
ret +=
((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) *
2.0) /
3.0;
ret +=
((20.0 * Math.sin(lat * PI) + 40.0 * Math.sin((lat / 3.0) * PI)) * 2.0) /
3.0;
ret +=
((160.0 * Math.sin((lat / 12.0) * PI) + 320 * Math.sin((lat * PI) / 30.0)) *
2.0) /
3.0;
return ret;
}
function transformlng(lng, lat) {
const PI = 3.1415926535897932384626;
let ret =
300.0 +
lng +
2.0 * lat +
0.1 * lng * lng +
0.1 * lng * lat +
0.1 * Math.sqrt(Math.abs(lng));
ret +=
((20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) *
2.0) /
3.0;
ret +=
((20.0 * Math.sin(lng * PI) + 40.0 * Math.sin((lng / 3.0) * PI)) * 2.0) /
3.0;
ret +=
((150.0 * Math.sin((lng / 12.0) * PI) +
300.0 * Math.sin((lng / 30.0) * PI)) *
2.0) /
3.0;
return ret;
}
//WSG84转BD09
export function wgs84togcj02tobd09(lng, lat) {
const xPI = (3.14159265358979324 * 3000.0) / 180.0;
const PI = 3.1415926535897932384626;
const a = 6378245.0;
const ee = 0.00669342162296594323;
// WGS84转GCj02
let dlat = transformlat(lng - 105.0, lat - 35.0);
let dlng = transformlng(lng - 105.0, lat - 35.0);
let radlat = (lat / 180.0) * PI;
let magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
let sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / (((a * (1 - ee)) / (magic * sqrtmagic)) * PI);
dlng = (dlng * 180.0) / ((a / sqrtmagic) * Math.cos(radlat) * PI);
let mglat = lat + dlat;
let mglng = lng + dlng;
// 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换
let z =
Math.sqrt(mglng * mglng + mglat * mglat) + 0.00002 * Math.sin(mglat * xPI);
let theta = Math.atan2(mglat, mglng) + 0.000003 * Math.cos(mglng * xPI);
let bdlng = z * Math.cos(theta) + 0.0065;
let bdlat = z * Math.sin(theta) + 0.006;
// return [bdlng, bdlat]
return { bd_lng: bdlng, bd_lat: bdlat };
}
export function dateFormat(fmt, date) {
let ret;
if (date) {
const time = new Date(date);
const opt = {
"Y+": time.getFullYear().toString(),
"m+": (time.getMonth() + 1).toString(),
"d+": time.getDate().toString(),
"H+": time.getHours().toString(),
"M+": time.getMinutes().toString(),
"S+": time.getSeconds().toString()
};
for (const k in opt) {
ret = new RegExp("(" + k + ")").exec(fmt);
if (ret) {
fmt = fmt.replace(
ret[1],
ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0")
);
}
}
return fmt;
}
}
import { createWebHistory, createRouter } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import AboutView from '../views/AboutView.vue'
import User from '../views/user.vue'
import Layout from '../views/layout/index.vue'
import Login from '../views/login/index.vue'
const routes = [
{
path: '/',
component: Layout,
children: [
{
path: '/dashboard',
component: AboutView,
meta: {
title: '首页',
},
},
{
path: '/2',
component: HomeView,
meta: {
title: '除尘器总览',
},
},
{
path: '/3',
component: AboutView,
meta: {
title: '设备管理',
},
},
{
path: '/4',
component: AboutView,
meta: {
title: '除尘器监控',
},
},
{
path: '/5',
component: AboutView,
meta: {
title: '告警总览',
},
},
{
path: '/6',
meta: {
title: '我的闭环',
},
children: [
{
path: 'profile',
component: HomeView,
meta: {
title: '我的待办',
},
},
{
path: 'posts',
component: AboutView,
meta: {
title: '我的已办',
},
},
],
},
]
},
{
path: '/login',
component: Login,
meta: {
title: '登录'
}
},
]
const router = createRouter({
history: createWebHistory(),
routes,
})
export {
routes,
router
}
\ No newline at end of file
* {
padding: 0;
margin: 0;
}
\ No newline at end of file
import Cookies from 'js-cookie'
const TokenKey = 'TOKEN'
export function getToken(TokenKey) {
return Cookies.get(TokenKey)
}
export function clearAllCookies() {
var keys = Object.keys(Cookies.get())
keys.forEach(item => Cookies.remove(item))
}
export function getCustomerName(customerName) {
return Cookies.get(customerName)
}
export function setToken(name,token) {
return Cookies.set(name, token)
}
export function removeToken(TokenKey) {
return Cookies.remove(TokenKey)
}
<template>
<div>about</div>
</template>
\ No newline at end of file
<template>
<div>
home
</div>
</template>
\ No newline at end of file
<template>
<div class="layout-box">
<div class="left">左侧侧边栏</div>
<div class="right">右侧头部</div>
</div>
</template>
<style scoped lang='scss'>
.layout-box {
display: flex;
height: 100vh;
.left {
width: 300px;
background: red;
}
.right {
width: calc(100% - 300px);
background: blue;
}
}
</style>
\ No newline at end of file
<template>
<div class="layout-box">
<div class="left">
<menuCom></menuCom>
</div>
<div class="right">
<div>头部</div>
<div>
<router-view />
</div>
</div>
</div>
</template>
<script>
import menuCom from './menuCom.vue'
export default {
components: {
menuCom
},
created() {
console.log('layout created')
}
}
</script>
<style scoped lang='scss'>
.layout-box {
display: flex;
height: 100vh;
.left {
max-width: 300px;
flex: 0;
}
.right {
flex: 1;
}
}
</style>
\ No newline at end of file
<template>
<el-row class="tac">
<el-col :span="24">
<el-menu
default-active="2"
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
>
<template v-for="(item, index) in routerList" :key="item.path">
<el-sub-menu
:index="index + ''"
v-if="item.children && item.children.length > 0"
>
<template #title>
<el-icon><location /></el-icon>
<span>{{getMenuTitle(item)}}</span>
</template>
<el-menu-item v-for="(item_son, index_son) in item.children" :key="item_son.path" :index="index + '_' + index_son">
<router-link :to="item.path + '/' + item_son.path">{{getMenuTitle(item_son)}}</router-link>
</el-menu-item>
</el-sub-menu>
<el-menu-item v-else :index="index + ''">
<el-icon><setting /></el-icon>
<span><router-link :to="item.path">{{getMenuTitle(item)}}</router-link></span>
</el-menu-item>
</template>
</el-menu>
</el-col>
</el-row>
</template>
<script setup>
import {
Document,
Menu as IconMenu,
Location,
Setting,
} from "@element-plus/icons-vue";
import {routes} from "../../router/index.js";
const routerList = routes[0].children;
const handleOpen = (key, keyPath) => {
console.log(key, keyPath);
};
const handleClose = (key, keyPath) => {
console.log(key, keyPath);
};
const getMenuTitle = (item) => {
return item.meta && item.meta.title || '未命名菜单'
}
</script>
<style scoped lang="scss">
a {
text-decoration: none;
color: #000000;
}
</style>
\ No newline at end of file
This diff is collapsed.
<template>
<div class="user">
<h2>User {{ $route.params.id }}</h2>
<router-view />
</div>
</template>
\ No newline at end of file
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from "path" // 需安装此模块
// https://vite.dev/config/
export default defineConfig(({mode}) => {
const ENV = loadEnv(mode, process.cwd())
return {
plugins: [vue()],
resolve: {
alias: {
// 这里就是需要配置resolve里的别名
"@": path.resolve(__dirname, "./src") // path记得引入
}
},
server: {
host: '0.0.0.0', // 指定服务器主机名
port: 3000, // 指定服务器端口
proxy: {
[ENV.VITE_APP_BASE_API5]: {
// target: 'https://screen.bmetech.com',
target: 'https://vis.bmetech.com',
changeOrigin: true,
},
}
}
}
})
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